Skip to content
Browse files

Initial revision. Modification of Rick Olson's original sentry plugin…

…. Stills needs more refactoring of Rick's tests and documentation to reflect the new string encryption usage.
  • Loading branch information...
0 parents commit 48a2ff0fc77cc66270df852502395b53d3f04c40 @obrie obrie committed Oct 24, 2006
58 CHANGELOG
@@ -0,0 +1,58 @@
+*0.3.1* (7 Jan 2006)
+
+* removed useless breakpoint [Solomon White]
+
+*0.3* (29 Oct 2005)
+
+* added rake task for generating asymmetric keys
+* Switch to migrations and schema for testing setup
+
+*0.2.9* (18 Sep 2005)
+
+* First RubyForge release.
+
+*0.2.8* (17 Sep 2005)
+
+* Added Active Record unit tests
+
+*0.2.7* (17 Sep 2005)
+
+* Added rdocs and stubs for AR unit tests
+
+*0.2.6* (2 Aug 2005)
+
+* Fixed generates_crypted so it adds attribute accessors
+
+*0.2.5* (27 Jul 2005)
+
+* Set ActiveRecord callback objects to only encrypt fields when they are not empty.
+
+*0.2.4* (11 Jul 2005)
+
+* Split ActiveRecord callback methods into their own classes.
+* Set AR virtual columns to fail silently on errors.
+
+*0.2.3* (11 Jul 2005)
+
+* Added ActiveRecord callback objects for SymmetricSentry and AsymmetricSentry. +one_way_encrypt+ is depreciated.
+* Readme doc added too
+
+*0.2.1* (9 Jul 2005)
+
+* vastly simplified one_way_encrypt at danp's suggestion. Use this in your model to try it out:
+
+ +one_way_encrypt :password*
+
+ That generates an SHA hash of model.password to model.crypted_password which is saved in the DB.
+ model.password is a virtual field. Continue using validates_confirmation_of for confirmation.
+
+
+*0.2* (9 Jul 2005)
+
+* added ActiveRecord::Base#one_way_encrypt class method to hash passwords with SHA
+* Renamed core classes to SymmetricSentry and AsymmetricSentry
+* Test Suite added
+
+*0.1*
+
+* Initial Import
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2005 Rick Olson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94 README
@@ -0,0 +1,94 @@
+= Sentry lib - painless encryption library
+
+Sentry is a simple wrapper around the mostly undocumented OpenSSL encryption classes.
+For now, look at the pseudo test cases in sentry.rb until I can get more examples written out.
+
+== Resources
+
+Install
+
+* gem install sentry
+
+Rubyforge project
+
+* http://rubyforge.org/projects/sentry
+
+RDocs
+
+* http://sentry.rubyforge.org
+
+Subversion
+
+* http://techno-weenie.net/svn/projects/sentry
+
+Collaboa
+
+* http://collaboa.techno-weenie.net/repository/browse/sentry
+
+== Using with ActiveRecord
+
+I wrote this for the purpose of encrypting ActiveRecord attributes. Just <tt>require 'sentry'</tt>, and some new
+class methods will be available to you:
+
+=== generates_crypted
+
+ generates_crypted :password, :mode => :sha | :symmetric | :asymmetric
+
+This is the generic class method to use. Default mode is :sha.
+
+=== generates_crypted_hash_of
+
+ generates_crypted_hash_of :password
+
+This is a shortcut for using SHA encryption. No different than specifying <tt>generates_crypted :password</tt>. In the above
+example, model.password is a virtual field, and the SHA hash is saved to model.crypted_password
+
+=== asymmetrically_encrypts
+
+ asymmetrically_encrypts :password
+
+This is a shortcut for using an asymmetrical algorithm with a private/public key file. To use this, generate a public and
+private key with Sentry::AsymmetricalSentry.save_random_rsa_key(private_key_file, public_key_file). If you want to encrypt the
+private key file with a symmetrical algorithm, pass a secret key (neither the key nor the decrypted value will be stored).
+
+ Sentry::AsymmetricSentry.save_random_rsa_key(private_key_file, public_key_file, :key => 'secret_password')
+
+What that does, is requires you to pass in that same secret password when accesing the method.
+
+ class Model < ActiveRecord::Base
+ generates_crypted :password, :mode => :asymmetric
+ end
+
+ model.password = '5234523453425'
+ model.save # password is encrypted and saved to crypted_password in the database,
+ # model.password is cleared and becomes a virtual field.
+ model.password('secret_password')
+ => '5234523453425'
+
+The public and private key file names can be set in config/environment.rb
+
+ Sentry::AsymmetricSentry.default_public_key_file = "#{RAILS_ROOT}/config/public.key"
+ Sentry::AsymmetricSentry.default_private_key_file = "#{RAILS_ROOT}/config/private.key"
+
+If the private key was encrypted with the Sentry::AsymmetricalSentry#save_random_rsa_key, you must provide that same key
+when accessing the AR model.
+
+=== symmetrically_encrypts
+
+ symmetrically_encrypts :password
+
+This is a shortcut for using a symmetrical algorithm with a secret password to encrypt the field.
+
+ class Model < ActiveRecord::Base
+ generates_crypted :password, :mode => :symmetric
+ end
+
+ model.password = '5234523453425'
+ model.save # password is encrypted and saved to crypted_password in the database,
+ # model.password is cleared and becomes a virtual field.
+ model.password
+ => '5234523453425'
+
+The secret password can be set in config/environment.rb
+
+ Sentry::SymmetricSentry.default_key = "secret_password"
42 RUNNING_UNIT_TESTS
@@ -0,0 +1,42 @@
+== Creating the test database
+
+The default name for the test databases is "encrypted_strings_plugin_test". If you
+want to use another database name then be sure to update the connection
+adapter setups you want to test with in test/database.yml.
+
+Make sure that you create database objects with the same user that you specified in i
+database.yml otherwise (on Postgres, at least) tests for default values will fail.
+
+== Running with Rake
+
+The easiest way to run the unit tests is through Rake. The default task runs
+the entire test suite for the sqlite adapter. You can also run the suite on just
+one adapter by passing the DB environment variable.
+
+ rake test DB=mysql
+
+For more information, checkout the full array of rake tasks with "rake -T"
+
+Rake can be found at http://rake.rubyforge.org
+
+== Running by hand
+
+Unit tests are located in test directory. If you only want to run a single test suite,
+or don't want to bother with Rake, you can do so with something like:
+
+ cd test; DB=mysql ruby base_test.rb
+
+That'll run the base suite using the MySQL adapter. Change the adapter
+and test suite name as needed.
+
+== Faster tests
+
+If you are using a database that supports transactions, you can set the
+"AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
+This gives a very large speed boost. With rake:
+
+ rake AR_TX_FIXTURES=yes
+
+Or, by hand:
+
+ AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
32 Rakefile
@@ -0,0 +1,32 @@
+require 'rake'
+require File.join('rake', 'testtask')
+require File.join('rake', 'rdoctask')
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the encrypted_strings plugin.'
+Rake::TestTask.new(:test) do |t|
+ # Dependency on other plugins requires this plugin to exist in an rails
+ # application
+ if (root_path = ENV['RAILS_ROOT']).nil?
+ root_path = File.dirname(File.expand_path(__FILE__))
+ while (boot_paths = Dir[File.join(root_path, 'config', 'boot{,.rb}')]).empty?
+ root_path = File.dirname(root_path)
+ end
+ end
+ Dir.chdir(root_path)
+
+ t.libs << 'lib'
+ t.pattern = File.join(File.dirname(__FILE__), 'test', '**', '*_test.rb')
+ t.verbose = true
+end
+
+desc 'Generate documentation for the encrypted_strings plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'EncryptedStrings'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include(File.join('lib', '**', '*.rb'))
+end
5 init.rb
@@ -0,0 +1,5 @@
+require 'encrypted_strings'
+
+class ::Integer #:nodoc:
+ include PluginAWeek::CoreExtensions::String::EncryptedStrings
+end
110 lib/encrypted_strings.rb
@@ -0,0 +1,110 @@
+#Copyright (c) 2005 Rick Olson
+#
+#Permission is hereby granted, free of charge, to any person obtaining
+#a copy of this software and associated documentation files (the
+#"Software"), to deal in the Software without restriction, including
+#without limitation the rights to use, copy, modify, merge, publish,
+#distribute, sublicense, and/or sell copies of the Software, and to
+#permit persons to whom the Software is furnished to do so, subject to
+#the following conditions:
+#
+#The above copyright notice and this permission notice shall be
+#included in all copies or substantial portions of the Software.
+#
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+#LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+#WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+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')
+
+class NoKeyError < StandardError
+end
+
+class NoPublicKeyError < StandardError
+end
+
+class NoPrivateKeyError < StandardError
+end
+
+module PluginAWeek #:nodoc:
+ module CoreExtensions #:nodoc:
+ module String #:nodoc:
+ module EncryptedStrings
+ #
+ #
+ def encrypt(mode = :sha, options = {})
+ options[:encrypt] = true
+
+ 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
+ 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
+ end
+
+ var_name = "@#{attr_name}"
+
+ # 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))
+ 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))
+ end
+ end
+ end
+ end
+end
112 lib/encrypted_strings/asymmetrically_encrypted_string.rb
@@ -0,0 +1,112 @@
+#
+#
+class AsymmetricallyEncryptedString < EncryptedString
+ #
+ @@default_private_key_file = nil
+ cattr_accessor :default_private_key_file
+
+ #
+ @@default_public_key_file = nil
+ cattr_accessor :default_public_key_file
+
+ #
+ @@default_symmetric_algorithm = nil
+ cattr_accessor :default_symmetric_algorithm
+
+ attr_reader :private_key_file
+ attr_reader :public_key_file
+ attr_accessor :symmetric_algorithm
+
+ # Configuration options:
+ # * <tt>private_key_file</tt> - encrypted private key file
+ # * <tt>public_key_file</tt> - public key file
+ # * <tt>symmetric_algorithm</tt> - algorithm to use for SymmetricSentry
+ #
+ def initialize(data, options = {})
+ options = options.symbolize_keys
+ options.assert_valid_keys(
+ :private_key_file,
+ :public_key_file,
+ :symmetric_algorithm,
+ :encrypt
+ )
+ options.reverse_merge!(
+ :private_key_file => @@default_private_key_file,
+ :public_key_file => @@default_public_key_file,
+ :symmetric_algorithm => @@default_symmetric_algorithm,
+ :encrypt => true
+ )
+
+ @public_key = @private_key = nil
+ private_key_file = options[:private_key_file]
+ public_key_file = options[:public_key_file]
+
+ super(encrypt(data))
+ end
+
+ def decrypt
+ raise NoPrivateKeyError unless private?
+
+ data = Base64.decode64(to_s)
+ private_rsa(@key).private_decrypt(data)
+ end
+
+ def private_key_file=(file)
+ @private_key_file = file and load_private_key
+ end
+
+ def public_key_file=(file)
+ @public_key_file = file and load_public_key
+ end
+
+ # Is this string encrypted using a public key?
+ def public?
+ return true unless @public_key.nil?
+ load_public_key and return @public_key
+ end
+
+ # Is this string encrypted using a private key?
+ def private?
+ return true unless @private_key.nil?
+ load_private_key and return @private_key
+ end
+
+ private
+ def encryptor
+ @encryptor ||= SymmetricSentry.new(:algorithm => @symmetric_algorithm)
+ end
+
+ def encrypt(data)
+ raise NoPublicKeyError unless public?
+
+ data = public_rsa.public_encrypt(data)
+ Base64.encode64(data)
+ end
+
+ def load_private_key #:nodoc:
+ @private_rsa = nil
+ @private_key_file ||= @@default_private_key_file
+ if @private_key_file and File.file?(@private_key_file)
+ @private_key = File.open(@private_key_file) { |f| f.read }
+ end
+ end
+
+ def load_public_key #:nodoc:
+ @public_rsa = nil
+ @public_key_file ||= @@default_public_key_file
+ if @public_key_file and File.file?(@public_key_file)
+ @public_key = File.open(@public_key_file) { |f| f.read }
+ end
+ end
+
+ # retrieves private rsa from encrypted private key
+ def private_rsa(key = nil)
+ return @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) unless key
+ OpenSSL::PKey::RSA.new(encryptor.decrypt_from_base64(@private_key, key))
+ end
+
+ # retrieves public rsa
+ def public_rsa
+ @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
+ end
+end
13 lib/encrypted_strings/encrypted_string.rb
@@ -0,0 +1,13 @@
+#
+#
+class EncryptedString < String
+ #
+ #
+ def ==(other)
+ if other.class == String
+ to_s == encrypt(other)
+ else
+ super
+ end
+ end
+end
79 lib/encrypted_strings/sentry.rb
@@ -0,0 +1,79 @@
+#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
33 lib/encrypted_strings/sha_encrypted_string.rb
@@ -0,0 +1,33 @@
+require 'digest/sha1'
+
+class SHAEncryptedString < EncryptedString
+ @@salt = 'salt'
+ cattr_accessor :salt
+
+ attr_accessor :salt
+
+ def initialize(data, options = {})
+ options = options.symbolize_keys
+ options.assert_valid_keys(
+ :salt,
+ :encrypt
+ )
+ options.reverse_merge!(
+ :salt => @@salt,
+ :encrypt => true
+ )
+ @salt = options[:salt]
+
+ super(options[:encrypt] ? encrypt(data) : data)
+ end
+
+ #
+ def decrypt
+ raise NotImplementedError, 'Cannot decrypt an SHA-Encrypted String'
+ end
+
+ private
+ def encrypt(data)
+ Digest::SHA1.hexdigest(data + @salt)
+ end
+end
62 lib/encrypted_strings/symmetrically_encrypted_string.rb
@@ -0,0 +1,62 @@
+#
+#
+class SymmetricallyEncryptedString < EncryptedString
+ #
+ @@default_algorithm = 'DES-EDE3-CBC'
+ cattr_accessor :default_algorithm
+
+ #
+ @@default_key = nil
+ cattr_accessor :default_key
+
+ attr_accessor :algorithm
+ attr_accessor :key
+
+ #
+ #
+ def initialize(data, options = {})
+ options = options.symbolize_keys
+ options.assert_valid_keys(
+ :algorithm,
+ :key,
+ :encrypt
+ )
+ options.reverse_merge!(
+ :algorithm => @@default_algorithm,
+ :key => @@default_key,
+ :encrypt => false
+ )
+
+ @key = options[:key]
+ raise NoKeyError if @key.nil?
+
+ @algorithm = options[:algorithm]
+
+ super(options[:encrypt] ? encrypt(data) : data)
+ end
+
+ #
+ #
+ def decrypt
+ des = encryptor
+ des.decrypt(@key)
+ text = des.update(Base64.decode64(to_s))
+ text << des.final
+ end
+
+ private
+ def encryptor #:nodoc:
+ @encryptor ||= OpenSSL::Cipher::Cipher.new(@algorithm)
+ end
+
+ #
+ #
+ def encrypt(data)
+ des = encryptor
+ des.encrypt(@key)
+ data = des.update(data)
+ data << des.final
+
+ Base64.encode64(data)
+ end
+end
9 tasks/encrypted_strings.rake
@@ -0,0 +1,9 @@
+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
+ Sentry::AsymmetricSentry.save_random_rsa_key(
+ ENV['PRIV'] || 'private.key',
+ ENV['PUB'] || 'public.key',
+ :key => ENV['KEY'])
+end
33 test/abstract_unit.rb
@@ -0,0 +1,33 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+
+require 'rubygems'
+require 'test/unit'
+require 'active_record'
+require 'active_record/fixtures'
+require 'active_support/binding_of_caller'
+require 'active_support/breakpoint'
+require "#{File.dirname(__FILE__)}/../lib/sentry"
+
+config_location = File.dirname(__FILE__) + '/database.yml'
+
+config = YAML::load(IO.read(config_location))
+ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
+ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite'])
+
+load(File.dirname(__FILE__) + "/schema.rb")
+
+Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
+Test::Unit::TestCase.use_instantiated_fixtures = false
+Test::Unit::TestCase.use_transactional_fixtures = (ENV['AR_TX_FIXTURES'] == "yes")
+$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
+
+class Test::Unit::TestCase #:nodoc:
+ def create_fixtures(*table_names)
+ if block_given?
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
+ else
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
+ end
+ end
+end
+
88 test/asymmetric_string_test.rb
@@ -0,0 +1,88 @@
+require 'abstract_unit'
+
+class AsymmetricSentryTest < Test::Unit::TestCase
+ def setup
+ @str = 'sentry'
+ @key = 'secret'
+ @public_key_file = File.dirname(__FILE__) + '/keys/public'
+ @private_key_file = File.dirname(__FILE__) + '/keys/private'
+ @encrypted_public_key_file = File.dirname(__FILE__) + '/keys/encrypted_public'
+ @encrypted_private_key_file = File.dirname(__FILE__) + '/keys/encrypted_private'
+ @sentry = Sentry::AsymmetricSentry.new
+
+ @orig = 'sentry'
+ @data = "vYfMxtVB8ezXmQKSNqTC9sPgi8TbsYRxWd7DVbpprzyuEdZ7gftJ/0IXsbXm\nXCU08bTAl0uEFm7dau+eJMXEJg==\n"
+ @encrypted_data = "q2obYAITmK93ylzVS01mJx1jSlnmylMX15nFpb4uKesVgnqvtzBRHZ/SK+Nm\nEzceIoAcJc3DHosVa4VUE/aK/A==\n"
+ Sentry::AsymmetricSentry.default_public_key_file = nil
+ Sentry::AsymmetricSentry.default_private_key_file = nil
+ end
+
+ def test_should_decrypt_files
+ set_key_files @public_key_file, @private_key_file
+ assert_equal @orig, @sentry.decrypt_from_base64(@data)
+ end
+
+ def test_should_decrypt_files_with_encrypted_key
+ set_key_files @encrypted_public_key_file, @encrypted_private_key_file
+ assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
+ end
+
+ def test_should_read_key_files
+ assert !@sentry.public?
+ assert !@sentry.private?
+ set_key_files @public_key_file, @private_key_file
+ end
+
+ def test_should_read_encrypted_key_files
+ assert !@sentry.public?
+ assert !@sentry.private?
+ set_key_files @encrypted_public_key_file, @encrypted_private_key_file
+ end
+
+ def test_should_decrypt_files_with_default_key
+ set_default_key_files @public_key_file, @private_key_file
+ assert_equal @orig, @sentry.decrypt_from_base64(@data)
+ end
+
+ def test_should_decrypt_files_with_default_encrypted_key
+ set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
+ assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
+ end
+
+ def test_should_decrypt_files_with_default_key_using_class_method
+ set_default_key_files @public_key_file, @private_key_file
+ assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@data)
+ end
+
+ def test_should_decrypt_files_with_default_encrypted_key_using_class_method
+ set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
+ assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@encrypted_data, @key)
+ end
+
+ def test_should_read_key_files_with_default_key
+ assert !@sentry.public?
+ assert !@sentry.private?
+ set_default_key_files @public_key_file, @private_key_file
+ end
+
+ def test_should_read_encrypted_key_files_with_default_key
+ assert !@sentry.public?
+ assert !@sentry.private?
+ set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
+ end
+
+ private
+ def set_key_files(public_key, private_key)
+ @sentry.public_key_file = public_key
+ @sentry.private_key_file = private_key
+ assert @sentry.private?
+ assert @sentry.public?
+ end
+
+ def set_default_key_files(public_key, private_key)
+ Sentry::AsymmetricSentry.default_public_key_file = public_key
+ Sentry::AsymmetricSentry.default_private_key_file = private_key
+ assert @sentry.private?
+ assert @sentry.public?
+ end
+end
18 test/database.yml
@@ -0,0 +1,18 @@
+sqlite:
+ :adapter: sqlite
+ :dbfile: sentry_plugin.sqlite.db
+sqlite3:
+ :adapter: sqlite3
+ :dbfile: sentry_plugin.sqlite3.db
+postgresql:
+ :adapter: postgresql
+ :username: postgres
+ :password: postgres
+ :database: sentry_plugin_test
+ :min_messages: ERROR
+mysql:
+ :adapter: mysql
+ :host: localhost
+ :username: rails
+ :password:
+ :database: sentry_plugin_test
25 test/fixtures/user.rb
@@ -0,0 +1,25 @@
+class User < ActiveRecord::Base
+ generates_crypted :creditcard, :mode => :asymmetric
+
+ def self.validates_password
+ validates_presence_of :crypted_password
+ validates_presence_of :password, :on => :create
+ validates_length_of :password, :in => 4..40
+ end
+end
+
+class ShaUser < User
+ validates_password
+ validates_confirmation_of :password
+ generates_crypted :password # sha is used by default
+end
+
+class DangerousUser < User # no password confirmation
+# validates_password
+ generates_crypted :password
+end
+
+class SymmetricUser < User
+ validates_password
+ generates_crypted :password, :mode => :symmetric
+end
11 test/fixtures/users.yml
@@ -0,0 +1,11 @@
+user_1:
+ id: 1
+ login: bob
+ crypted_password: "0XlmUuNpE2k=\n"
+ crypted_creditcard: "vYfMxtVB8ezXmQKSNqTC9sPgi8TbsYRxWd7DVbpprzyuEdZ7gftJ/0IXsbXm\nXCU08bTAl0uEFm7dau+eJMXEJg==\n"
+ type: SymmetricUser
+user_2:
+ id: 2
+ login: fred
+ crypted_creditcard: "q2obYAITmK93ylzVS01mJx1jSlnmylMX15nFpb4uKesVgnqvtzBRHZ/SK+Nm\nEzceIoAcJc3DHosVa4VUE/aK/A==\n"
+
12 test/keys/encrypted_private
@@ -0,0 +1,12 @@
+OBNa1q8kbx8pyZZjIpr/pZV0oulE2czh5JlPW/13XsBvoz+A2zxA9gchhi6c
+3yvfqgcZdojcsep+IiTqeg3gOPB2xNbedpP1lm+9tEfgdb9r1CLzRcURh7Hg
+ufWgyEkS0lloz/YLy4hg9YDKetFNF9fnrk3xVwZPwFVuk4l/Unw1FTXLHsrq
+KG27cR8mvNOow4bk4LVhk/avFSM85m3ITySEnyJsQQDzsI/RrWcQ7Js+8Ynv
+esN51E/T0CYtkMEne2zSaD5qUTJlQ7Qtn4UUeZkpYjn4xQZPxw4OjL6zofg7
+lsqElSv1/qP3QI8aKcQQklVsHRc5AgsxOFX4J6g6lo4kOGOwn0Ex8IRDfOej
+pq4SUDh9IXz+6FBieQrObB/xEsKysVwRSzXre6ObHlPFsigg5ekFPyCv5ZTz
+0iP8+xe/FJRrYdR3r3F5pRkOy0pw9EqlrLjmOx3/fgxhLq8FWmcSBbH3h3SG
+GkJlfHNjF77FTJjnHKzRS+5VpdW4IHbsjL+NlI1z9Ol//czYvSGv85NdJvkq
+PmH3o0+uYdwY5PeSMOPV21nJ3dwiKlm5IMFasL3C5yVJNVTVZTS7vWdcgZ4U
+XfWQ9Y266ibbqXPluv4nxt1+kgjxmPbjPdYrlB5t7a2+unzT3oE3f4VGOG+k
+YqFg0ErHN+fu
4 test/keys/encrypted_public
@@ -0,0 +1,4 @@
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQCvktJgveIcgTH98hAhMjo0g6/GVMJaYdUh+/zQn4RBWASRmwEfJqggsfKT
+pSNendZQMD8kKS8J1YTBr60ToM25AgMBAAE=
+-----END RSA PUBLIC KEY-----
9 test/keys/private
@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBAL/xeY6aqFx6z1ThNOwgPgxv3tsonTlCj8VkN3Ikumg6SzBuLxlV
+i9gFQZ7K9Pv9o/7+xUTYODqBpVhwgLBeu2cCAwEAAQJAHyjFMfg7Yp/xLndMzxRA
+3mX+yJckRtpeWo31TktWE3syks1r9OrfmxKiStM9kFRubeBHTihZrW92TYkROLxh
+uQIhAPuftVTJZFDNxeYDKIMIMqwR8KZgtuf25cv4pTxYwPqLAiEAw0gNwDJHBkvo
+da4402pZNQmBA6qCSf0svDXqoEoaShUCIGBma340Oe6LJ0pb42Vv+pnZtazIWMq9
+2IQwmn1oM2bJAiEAhgP869mVRIzzi091UCG79tn+4DU0FPLasI+P5VD1mcECIQDb
+3ndvbPcElVvdJgabxyWJJsNtBBNZYPsuc6NrQyShOw==
+-----END RSA PRIVATE KEY-----
4 test/keys/public
@@ -0,0 +1,4 @@
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQC/8XmOmqhces9U4TTsID4Mb97bKJ05Qo/FZDdyJLpoOkswbi8ZVYvYBUGe
+yvT7/aP+/sVE2Dg6gaVYcICwXrtnAgMBAAE=
+-----END RSA PUBLIC KEY-----
10 test/schema.rb
@@ -0,0 +1,10 @@
+ActiveRecord::Schema.define(:version => 1) do
+
+ create_table "users", :force => true do |t|
+ t.column :crypted_password, :string, :limit => 255
+ t.column :crypted_creditcard, :string, :limit => 255
+ t.column :login, :string, :limit => 50
+ t.column :type, :string, :limit => 20
+ end
+
+end
31 test/sha_string_test.rb
@@ -0,0 +1,31 @@
+require 'abstract_unit'
+require 'fixtures/user'
+
+class ShaSentryTest < Test::Unit::TestCase
+ def setup
+ Sentry::ShaSentry.salt = 'salt'
+ end
+
+ def test_should_encrypt
+ assert_equal 'f438229716cab43569496f3a3630b3727524b81b', Sentry::ShaSentry.encrypt('test')
+ end
+
+ def test_should_encrypt_with_salt
+ Sentry::ShaSentry.salt = 'different salt'
+ assert_equal '18e3256d71529db8fa65b2eef24a69ddad7070f3', Sentry::ShaSentry.encrypt('test')
+ end
+
+ def test_should_encrypt_user_password
+ u = ShaUser.new :login => 'bob'
+ u.password = u.password_confirmation = 'test'
+ assert u.save
+ assert u.crypted_password = 'f438229716cab43569496f3a3630b3727524b81b'
+ end
+
+ def test_should_encrypt_user_password_without_confirmation
+ u = DangerousUser.new :login => 'bob'
+ u.password = 'test'
+ assert u.save
+ assert u.crypted_password = 'f438229716cab43569496f3a3630b3727524b81b'
+ end
+end
37 test/symmetric_string_test.rb
@@ -0,0 +1,37 @@
+require 'abstract_unit'
+
+class SymmetricSentryTest < Test::Unit::TestCase
+ def setup
+ @str = 'sentry'
+ @key = 'secret'
+ @encrypted = "0XlmUuNpE2k=\n"
+ @sentry = Sentry::SymmetricSentry.new
+ Sentry::SymmetricSentry.default_key = nil
+ end
+
+ def test_should_encrypt
+ assert_equal @encrypted, @sentry.encrypt_to_base64(@str, @key)
+ end
+
+ def test_should_decrypt
+ assert_equal @str, @sentry.decrypt_from_base64(@encrypted, @key)
+ end
+
+ def test_should_encrypt_with_default_key
+ Sentry::SymmetricSentry.default_key = @key
+ assert_equal @encrypted, @sentry.encrypt_to_base64(@str)
+ end
+
+ def test_should_decrypt_with_default_key
+ Sentry::SymmetricSentry.default_key = @key
+ assert_equal @str, @sentry.decrypt_from_base64(@encrypted)
+ end
+
+ def test_should_raise_error_when_encrypt_with_no_key
+ assert_raises(Sentry::NoKeyError) { @sentry.encrypt_to_base64(@str) }
+ end
+
+ def test_should_raise_error_when_decrypt_with_no_key
+ assert_raises(Sentry::NoKeyError) { @sentry.decrypt_from_base64(@str) }
+ end
+end
2 test/tests.rb
@@ -0,0 +1,2 @@
+$:.unshift "../lib"
+Dir["**/*_test.rb"].each { |f| load f }

0 comments on commit 48a2ff0

Please sign in to comment.
Something went wrong with that request. Please try again.