sentry 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +45 -0
- data/MIT-LICENSE +20 -0
- data/README +80 -0
- data/RUNNING_UNIT_TESTS +41 -0
- data/lib/active_record/sentry.rb +79 -0
- data/lib/sentry.rb +46 -0
- data/lib/sentry/asymmetric_sentry.rb +144 -0
- data/lib/sentry/asymmetric_sentry_callback.rb +17 -0
- data/lib/sentry/sha_sentry.rb +41 -0
- data/lib/sentry/symmetric_sentry.rb +79 -0
- data/lib/sentry/symmetric_sentry_callback.rb +17 -0
- data/test/abstract_unit.rb +25 -0
- data/test/asymmetric_sentry_callback_test.rb +61 -0
- data/test/asymmetric_sentry_test.rb +88 -0
- data/test/connections/native_db2/connection.rb +14 -0
- data/test/connections/native_mysql/connection.rb +14 -0
- data/test/connections/native_oci/connection.rb +15 -0
- data/test/connections/native_postgresql/connection.rb +14 -0
- data/test/connections/native_sqlite/connection.rb +34 -0
- data/test/connections/native_sqlite3/connection.rb +33 -0
- data/test/connections/native_sqlserver/connection.rb +14 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +15 -0
- data/test/fixtures/activerecord_sentry.sqlite +0 -0
- data/test/fixtures/activerecord_sentry.sqlite3 +0 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +1 -0
- data/test/fixtures/db_definitions/postgresql.sql +8 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +7 -0
- data/test/fixtures/user.rb +25 -0
- data/test/fixtures/users.yml +11 -0
- data/test/keys/encrypted_private +12 -0
- data/test/keys/encrypted_public +4 -0
- data/test/keys/private +9 -0
- data/test/keys/public +4 -0
- data/test/sha_sentry_test.rb +31 -0
- data/test/symmetric_sentry_callback_test.rb +33 -0
- data/test/symmetric_sentry_test.rb +37 -0
- data/test/tests.rb +2 -0
- metadata +89 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
*0.2.8* (17 Sep 2005)
|
2
|
+
|
3
|
+
* Added Active Record unit tests
|
4
|
+
|
5
|
+
*0.2.7* (17 Sep 2005)
|
6
|
+
|
7
|
+
* Added rdocs and stubs for AR unit tests
|
8
|
+
|
9
|
+
*0.2.6* (2 Aug 2005)
|
10
|
+
|
11
|
+
* Fixed generates_crypted so it adds attribute accessors
|
12
|
+
|
13
|
+
*0.2.5* (27 Jul 2005)
|
14
|
+
|
15
|
+
* Set ActiveRecord callback objects to only encrypt fields when they are not empty.
|
16
|
+
|
17
|
+
*0.2.4* (11 Jul 2005)
|
18
|
+
|
19
|
+
* Split ActiveRecord callback methods into their own classes.
|
20
|
+
* Set AR virtual columns to fail silently on errors.
|
21
|
+
|
22
|
+
*0.2.3* (11 Jul 2005)
|
23
|
+
|
24
|
+
* Added ActiveRecord callback objects for SymmetricSentry and AsymmetricSentry. +one_way_encrypt+ is depreciated.
|
25
|
+
* Readme doc added too
|
26
|
+
|
27
|
+
*0.2.1* (9 Jul 2005)
|
28
|
+
|
29
|
+
* vastly simplified one_way_encrypt at danp's suggestion. Use this in your model to try it out:
|
30
|
+
|
31
|
+
+one_way_encrypt :password*
|
32
|
+
|
33
|
+
That generates an SHA hash of model.password to model.crypted_password which is saved in the DB.
|
34
|
+
model.password is a virtual field. Continue using validates_confirmation_of for confirmation.
|
35
|
+
|
36
|
+
|
37
|
+
*0.2* (9 Jul 2005)
|
38
|
+
|
39
|
+
* added ActiveRecord::Base#one_way_encrypt class method to hash passwords with SHA
|
40
|
+
* Renamed core classes to SymmetricSentry and AsymmetricSentry
|
41
|
+
* Test Suite added
|
42
|
+
|
43
|
+
*0.1*
|
44
|
+
|
45
|
+
* Initial Import
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005 Rick Olson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= Sentry lib - painless encryption library
|
2
|
+
|
3
|
+
Sentry is a simple wrapper around the mostly undocumented OpenSSL encryption classes.
|
4
|
+
For now, look at the pseudo test cases in sentry.rb until I can get more examples written out.
|
5
|
+
|
6
|
+
== Download
|
7
|
+
|
8
|
+
Sentry is distributed as a gem:
|
9
|
+
|
10
|
+
gem install --source http://techno-weenie.net/code sentry
|
11
|
+
|
12
|
+
Please email any comments or changes for code to technoweenie AT gmail.com
|
13
|
+
|
14
|
+
== Using with ActiveRecord
|
15
|
+
|
16
|
+
I wrote this for the purpose of encrypting ActiveRecord attributes. Just <tt>require 'sentry'</tt>, and some new
|
17
|
+
class methods will be available to you:
|
18
|
+
|
19
|
+
=== generates_crypted
|
20
|
+
|
21
|
+
generates_crypted :password, :mode => :sha | :symmetric | :asymmetric
|
22
|
+
|
23
|
+
This is the generic class method to use. Default mode is :sha.
|
24
|
+
|
25
|
+
=== generates_crypted_hash_of
|
26
|
+
|
27
|
+
generates_crypted_hash_of :password
|
28
|
+
|
29
|
+
This is a shortcut for using SHA encryption. No different than specifying <tt>generates_crypted :password</tt>. In the above
|
30
|
+
example, model.password is a virtual field, and the SHA hash is saved to model.crypted_password
|
31
|
+
|
32
|
+
=== asymmetrically_encrypts
|
33
|
+
|
34
|
+
asymmetrically_encrypts :password
|
35
|
+
|
36
|
+
This is a shortcut for using an asymmetrical algorithm with a private/public key file. To use this, generate a public and
|
37
|
+
private key with Sentry::AsymmetricalSentry.save_random_rsa_key(private_key_file, public_key_file). If you want to encrypt the
|
38
|
+
private key file with a symmetrical algorithm, pass a secret key (neither the key nor the decrypted value will be stored).
|
39
|
+
|
40
|
+
Sentry::AsymmetricalSentry.save_random_rsa_key(private_key_file, public_key_file, :key => 'secret_password')
|
41
|
+
|
42
|
+
What that does, is requires you to pass in that same secret password when accesing the method.
|
43
|
+
|
44
|
+
class Model < ActiveRecord::Base
|
45
|
+
generates_crypted :password, :mode => :asymmetric
|
46
|
+
end
|
47
|
+
|
48
|
+
model.password = '5234523453425'
|
49
|
+
model.save # password is encrypted and saved to crypted_password in the database,
|
50
|
+
# model.password is cleared and becomes a virtual field.
|
51
|
+
model.password('secret_password')
|
52
|
+
=> '5234523453425'
|
53
|
+
|
54
|
+
The public and private key file names can be set in config/environment.rb
|
55
|
+
|
56
|
+
Sentry::AsymmetricSentry.default_public_key_file = "#{RAILS_ROOT}/config/public.key"
|
57
|
+
Sentry::AsymmetricSentry.default_private_key_file = "#{RAILS_ROOT}/config/private.key"
|
58
|
+
|
59
|
+
If the private key was encrypted with the Sentry::AsymmetricalSentry#save_random_rsa_key, you must provide that same key
|
60
|
+
when accessing the AR model.
|
61
|
+
|
62
|
+
=== symmetrically_encrypts
|
63
|
+
|
64
|
+
symmetrically_encrypts :password
|
65
|
+
|
66
|
+
This is a shortcut for using a symmetrical algorithm with a secret password to encrypt the field.
|
67
|
+
|
68
|
+
class Model < ActiveRecord::Base
|
69
|
+
generates_crypted :password, :mode => :symmetric
|
70
|
+
end
|
71
|
+
|
72
|
+
model.password = '5234523453425'
|
73
|
+
model.save # password is encrypted and saved to crypted_password in the database,
|
74
|
+
# model.password is cleared and becomes a virtual field.
|
75
|
+
model.password
|
76
|
+
=> '5234523453425'
|
77
|
+
|
78
|
+
The secret password can be set in config/environment.rb
|
79
|
+
|
80
|
+
Sentry::SymmetricSentry.default_key = "secret_password"
|
data/RUNNING_UNIT_TESTS
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
== Creating the test database
|
2
|
+
|
3
|
+
The default name for the test databases is "activerecord_sentry". If you
|
4
|
+
want to use another database name then be sure to update the connection
|
5
|
+
adapter setups you want to test with in test/connections/<your database>/connection.rb.
|
6
|
+
When you have the database online, you can import the fixture tables with
|
7
|
+
the test/fixtures/db_definitions/*.sql files.
|
8
|
+
|
9
|
+
Make sure that you create database objects with the same user that you specified in i
|
10
|
+
connection.rb otherwise (on Postgres, at least) tests for default values will fail.
|
11
|
+
|
12
|
+
== Running with Rake
|
13
|
+
|
14
|
+
The easiest way to run the unit tests is through Rake. The default task runs
|
15
|
+
the entire test suite for all the adapters. You can also run the suite on just
|
16
|
+
one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
|
17
|
+
or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
|
18
|
+
|
19
|
+
Rake can be found at http://rake.rubyforge.org
|
20
|
+
|
21
|
+
== Running by hand
|
22
|
+
|
23
|
+
Unit tests are located in test directory. If you only want to run a single test suite,
|
24
|
+
or don't want to bother with Rake, you can do so with something like:
|
25
|
+
|
26
|
+
cd test; ruby -I "connections/native_mysql" base_test.rb
|
27
|
+
|
28
|
+
That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
|
29
|
+
and test suite name as needed.
|
30
|
+
|
31
|
+
== Faster tests
|
32
|
+
|
33
|
+
If you are using a database that supports transactions, you can set the
|
34
|
+
"AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
|
35
|
+
This gives a very large speed boost. With rake:
|
36
|
+
|
37
|
+
rake AR_TX_FIXTURES=yes
|
38
|
+
|
39
|
+
Or, by hand:
|
40
|
+
|
41
|
+
AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module Sentry
|
3
|
+
def self.included(base) # :nodoc:
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def generates_crypted(attr_name, options = {})
|
9
|
+
mode = options[:mode] || :sha
|
10
|
+
case mode
|
11
|
+
when :sha
|
12
|
+
generates_crypted_hash_of(attr_name)
|
13
|
+
when :asymmetric, :asymmetrical
|
14
|
+
asymmetrically_encrypts(attr_name)
|
15
|
+
when :symmetric, :symmetrical
|
16
|
+
symmetrically_encrypts(attr_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def generates_crypted_hash_of(attribute)
|
21
|
+
before_validation ::Sentry::ShaSentry.new(attribute)
|
22
|
+
attr_accessor attribute
|
23
|
+
end
|
24
|
+
|
25
|
+
def asymmetrically_encrypts(attr_name)
|
26
|
+
temp_sentry = ::Sentry::AsymmetricSentryCallback.new(attr_name)
|
27
|
+
before_validation temp_sentry
|
28
|
+
after_save temp_sentry
|
29
|
+
|
30
|
+
define_method(attr_name) do |*optional|
|
31
|
+
send("#{attr_name}!", *optional) rescue nil
|
32
|
+
end
|
33
|
+
|
34
|
+
define_method("#{attr_name}!") do |*optional|
|
35
|
+
return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
|
36
|
+
return nil if send("crypted_#{attr_name}").nil?
|
37
|
+
key = optional.shift
|
38
|
+
::Sentry::AsymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"), key)
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method("#{attr_name}=") do |value|
|
42
|
+
decrypted_values[attr_name] = value
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
define_method(:decrypted_values) do
|
48
|
+
@decrypted_values ||= {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def symmetrically_encrypts(attr_name)
|
53
|
+
temp_sentry = ::Sentry::SymmetricSentryCallback.new(attr_name)
|
54
|
+
before_validation temp_sentry
|
55
|
+
after_save temp_sentry
|
56
|
+
|
57
|
+
define_method(attr_name) do
|
58
|
+
send("#{attr_name}!") rescue nil
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method("#{attr_name}!") do
|
62
|
+
return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
|
63
|
+
return nil if send("crypted_#{attr_name}").nil?
|
64
|
+
::Sentry::SymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"))
|
65
|
+
end
|
66
|
+
|
67
|
+
define_method("#{attr_name}=") do |value|
|
68
|
+
decrypted_values[attr_name] = value
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
define_method(:decrypted_values) do
|
74
|
+
@decrypted_values ||= {}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/sentry.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#Copyright (c) 2005 Rick Olson
|
2
|
+
#
|
3
|
+
#Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
#a copy of this software and associated documentation files (the
|
5
|
+
#"Software"), to deal in the Software without restriction, including
|
6
|
+
#without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
#distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
#permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
#the following conditions:
|
10
|
+
#
|
11
|
+
#The above copyright notice and this permission notice shall be
|
12
|
+
#included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
#LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
#WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'openssl'
|
23
|
+
require 'base64'
|
24
|
+
require 'sentry/symmetric_sentry'
|
25
|
+
require 'sentry/asymmetric_sentry'
|
26
|
+
require 'sentry/sha_sentry'
|
27
|
+
require 'sentry/symmetric_sentry_callback'
|
28
|
+
require 'sentry/asymmetric_sentry_callback'
|
29
|
+
|
30
|
+
module Sentry
|
31
|
+
class NoKeyError < StandardError
|
32
|
+
end
|
33
|
+
class NoPublicKeyError < StandardError
|
34
|
+
end
|
35
|
+
class NoPrivateKeyError < StandardError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
require 'active_record/sentry'
|
41
|
+
ActiveRecord::Base.class_eval do
|
42
|
+
include ActiveRecord::Sentry
|
43
|
+
end
|
44
|
+
rescue NameError
|
45
|
+
nil
|
46
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Sentry
|
2
|
+
class AsymmetricSentry
|
3
|
+
attr_reader :private_key_file
|
4
|
+
attr_reader :public_key_file
|
5
|
+
attr_accessor :symmetric_algorithm
|
6
|
+
@@default_private_key_file = nil
|
7
|
+
@@default_public_key_file = nil
|
8
|
+
@@default_symmetric_algorithm = nil
|
9
|
+
|
10
|
+
# available options:
|
11
|
+
# * <tt>:private_key_file</tt> - encrypted private key file
|
12
|
+
# * <tt>:public_key_file</tt> - public key file
|
13
|
+
# * <tt>:symmetric_algorithm</tt> - algorithm to use for SymmetricSentry
|
14
|
+
def initialize(options = {})
|
15
|
+
@public_key = @private_key = nil
|
16
|
+
private_key_file = options[:private_key_file]
|
17
|
+
public_key_file = options[:public_key_file] || @@default_public_key_file
|
18
|
+
@symmetric_algorithm = options[:symmetric_algorithm] || @@default_symmetric_algorithm
|
19
|
+
end
|
20
|
+
|
21
|
+
def encrypt(data)
|
22
|
+
raise NoPublicKeyError unless public?
|
23
|
+
public_rsa.public_encrypt(data)
|
24
|
+
end
|
25
|
+
|
26
|
+
def encrypt_to_base64(data)
|
27
|
+
Base64.encode64(encrypt(data))
|
28
|
+
end
|
29
|
+
|
30
|
+
def decrypt(data, key = nil)
|
31
|
+
raise NoPrivateKeyError unless private?
|
32
|
+
private_rsa(key).private_decrypt(data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def decrypt_from_base64(data, key = nil)
|
36
|
+
decrypt(Base64.decode64(data), key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def private_key_file=(file)
|
40
|
+
@private_key_file = file and load_private_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def public_key_file=(file)
|
44
|
+
@public_key_file = file and load_public_key
|
45
|
+
end
|
46
|
+
|
47
|
+
def public?
|
48
|
+
return true unless @public_key.nil?
|
49
|
+
load_public_key and return @public_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def private?
|
53
|
+
return true unless @private_key.nil?
|
54
|
+
load_private_key and return @private_key
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
# * <tt>:key</tt> - secret password
|
59
|
+
# * <tt>:symmetric_algorithm</tt> - symmetrical algorithm to use
|
60
|
+
def save_random_rsa_key(private_key_file, public_key_file, options = {})
|
61
|
+
rsa = OpenSSL::PKey::RSA.new(512)
|
62
|
+
public_key = rsa.public_key
|
63
|
+
private_key = options[:key] ?
|
64
|
+
SymmetricSentry.new(:algorithm => options[:symmetric_algorithm]).encrypt_to_base64(rsa.to_s, options[:key]) :
|
65
|
+
rsa.to_s
|
66
|
+
File.open(public_key_file, 'w') { |f| f.write(public_key) }
|
67
|
+
File.open(private_key_file, 'w') { |f| f.write(private_key) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def encrypt(data)
|
71
|
+
self.new.encrypt(data)
|
72
|
+
end
|
73
|
+
|
74
|
+
def encrypt_to_base64(data)
|
75
|
+
self.new.encrypt_to_base64(data)
|
76
|
+
end
|
77
|
+
|
78
|
+
def decrypt(data, key = nil)
|
79
|
+
self.new.decrypt(data, key)
|
80
|
+
end
|
81
|
+
|
82
|
+
def decrypt_from_base64(data, key = nil)
|
83
|
+
self.new.decrypt_from_base64(data, key)
|
84
|
+
end
|
85
|
+
|
86
|
+
# cattr_accessor would be lovely
|
87
|
+
def default_private_key_file
|
88
|
+
@@default_private_key_file
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_private_key_file=(value)
|
92
|
+
@@default_private_key_file = value
|
93
|
+
end
|
94
|
+
|
95
|
+
def default_public_key_file
|
96
|
+
@@default_public_key_file
|
97
|
+
end
|
98
|
+
|
99
|
+
def default_public_key_file=(value)
|
100
|
+
@@default_public_key_file = value
|
101
|
+
end
|
102
|
+
|
103
|
+
def default_symmetric_algorithm
|
104
|
+
@@default_symmetric_algorithm
|
105
|
+
end
|
106
|
+
|
107
|
+
def default_symmetric_algorithm=(value)
|
108
|
+
@@default_symmetric_algorithm = value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def encryptor
|
114
|
+
@encryptor ||= SymmetricSentry.new(:algorithm => @symmetric_algorithm)
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_private_key
|
118
|
+
@private_rsa = nil
|
119
|
+
@private_key_file ||= @@default_private_key_file
|
120
|
+
if @private_key_file and File.file?(@private_key_file)
|
121
|
+
@private_key = File.open(@private_key_file) { |f| f.read }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def load_public_key
|
126
|
+
@public_rsa = nil
|
127
|
+
@public_key_file ||= @@default_public_key_file
|
128
|
+
if @public_key_file and File.file?(@public_key_file)
|
129
|
+
@public_key = File.open(@public_key_file) { |f| f.read }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# retrieves private rsa from encrypted private key
|
134
|
+
def private_rsa(key = nil)
|
135
|
+
return @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) unless key
|
136
|
+
OpenSSL::PKey::RSA.new(encryptor.decrypt_from_base64(@private_key, key))
|
137
|
+
end
|
138
|
+
|
139
|
+
# retrieves public rsa
|
140
|
+
def public_rsa
|
141
|
+
@public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sentry
|
2
|
+
class AsymmetricSentryCallback
|
3
|
+
def initialize(attr_name)
|
4
|
+
@attr_name = attr_name
|
5
|
+
end
|
6
|
+
|
7
|
+
# Performs encryption on before_validation Active Record callback
|
8
|
+
def before_validation(model)
|
9
|
+
return if model.send(@attr_name).blank?
|
10
|
+
model.send("crypted_#{@attr_name}=", AsymmetricSentry.encrypt_to_base64(model.send(@attr_name)))
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_save(model)
|
14
|
+
model.send("#{@attr_name}=", nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
module Sentry
|
3
|
+
class ShaSentry
|
4
|
+
@@salt = 'salt'
|
5
|
+
attr_accessor :salt
|
6
|
+
|
7
|
+
# Encrypts data using SHA.
|
8
|
+
def encrypt(data)
|
9
|
+
self.class.encrypt(data + salt.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Initialize the class.
|
13
|
+
# Used by ActiveRecord::Base#generates_crypted to set up as a callback object for a model
|
14
|
+
def initialize(attribute = nil)
|
15
|
+
@attribute = attribute
|
16
|
+
end
|
17
|
+
|
18
|
+
# Performs encryption on before_validation Active Record callback
|
19
|
+
def before_validation(model)
|
20
|
+
return unless model.send(@attribute)
|
21
|
+
model.send("crypted_#{@attribute}=", encrypt(model.send(@attribute)))
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Gets the class salt value used when encrypting
|
26
|
+
def salt
|
27
|
+
@@salt
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the class salt value used when encrypting
|
31
|
+
def salt=(value)
|
32
|
+
@@salt = value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Encrypts the data
|
36
|
+
def encrypt(data)
|
37
|
+
Digest::SHA1.hexdigest(data + @@salt)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Sentry
|
2
|
+
class SymmetricSentry
|
3
|
+
@@default_algorithm = 'DES-EDE3-CBC'
|
4
|
+
@@default_key = nil
|
5
|
+
attr_accessor :algorithm
|
6
|
+
def initialize(options = {})
|
7
|
+
@algorithm = options[:algorithm] || @@default_algorithm
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypt(data, key = nil)
|
11
|
+
key = check_for_key!(key)
|
12
|
+
des = encryptor
|
13
|
+
des.encrypt(key)
|
14
|
+
data = des.update(data)
|
15
|
+
data << des.final
|
16
|
+
end
|
17
|
+
|
18
|
+
def encrypt_to_base64(text, key = nil)
|
19
|
+
Base64.encode64(encrypt(text, key))
|
20
|
+
end
|
21
|
+
|
22
|
+
def decrypt(data, key = nil)
|
23
|
+
key = check_for_key!(key)
|
24
|
+
des = encryptor
|
25
|
+
des.decrypt(key)
|
26
|
+
text = des.update(data)
|
27
|
+
text << des.final
|
28
|
+
end
|
29
|
+
|
30
|
+
def decrypt_from_base64(text, key = nil)
|
31
|
+
decrypt(Base64.decode64(text), key)
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def default_algorithm
|
36
|
+
@@default_algorithm
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_algorithm=(value)
|
40
|
+
@@default_algorithm = value
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_key
|
44
|
+
@@default_key
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_key=(value)
|
48
|
+
@@default_key = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def encrypt(data, key = nil)
|
52
|
+
self.new.encrypt(data, key)
|
53
|
+
end
|
54
|
+
|
55
|
+
def encrypt_to_base64(text, key = nil)
|
56
|
+
self.new.encrypt_to_base64(text, key)
|
57
|
+
end
|
58
|
+
|
59
|
+
def decrypt(data, key = nil)
|
60
|
+
self.new.decrypt(data, key)
|
61
|
+
end
|
62
|
+
|
63
|
+
def decrypt_from_base64(text, key = nil)
|
64
|
+
self.new.decrypt_from_base64(text, key)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def encryptor
|
70
|
+
@encryptor ||= OpenSSL::Cipher::Cipher.new(@algorithm)
|
71
|
+
end
|
72
|
+
|
73
|
+
def check_for_key!(key)
|
74
|
+
valid_key = key || @@default_key
|
75
|
+
raise Sentry::NoKeyError if valid_key.nil?
|
76
|
+
valid_key
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sentry
|
2
|
+
class SymmetricSentryCallback
|
3
|
+
def initialize(attr_name)
|
4
|
+
@attr_name = attr_name
|
5
|
+
end
|
6
|
+
|
7
|
+
# Performs encryption on before_validation Active Record callback
|
8
|
+
def before_validation(model)
|
9
|
+
return if model.send(@attr_name).blank?
|
10
|
+
model.send("crypted_#{@attr_name}=", SymmetricSentry.encrypt_to_base64(model.send(@attr_name)))
|
11
|
+
end
|
12
|
+
|
13
|
+
def after_save(model)
|
14
|
+
model.send("#{@attr_name}=", nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'active_record'
|
6
|
+
require 'active_record/fixtures'
|
7
|
+
require 'active_support/binding_of_caller'
|
8
|
+
require 'active_support/breakpoint'
|
9
|
+
require 'connection'
|
10
|
+
require 'sentry'
|
11
|
+
|
12
|
+
class Test::Unit::TestCase #:nodoc:
|
13
|
+
def create_fixtures(*table_names)
|
14
|
+
if block_given?
|
15
|
+
Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names) { yield }
|
16
|
+
else
|
17
|
+
Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
23
|
+
Test::Unit::TestCase.use_instantiated_fixtures = false
|
24
|
+
Test::Unit::TestCase.use_transactional_fixtures = (ENV['AR_TX_FIXTURES'] == "yes")
|
25
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
class AsymmetricSentryCallbackTest < Test::Unit::TestCase
|
5
|
+
fixtures :users
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@str = 'sentry'
|
9
|
+
@key = 'secret'
|
10
|
+
@public_key_file = File.dirname(__FILE__) + '/keys/public'
|
11
|
+
@private_key_file = File.dirname(__FILE__) + '/keys/private'
|
12
|
+
@encrypted_public_key_file = File.dirname(__FILE__) + '/keys/encrypted_public'
|
13
|
+
@encrypted_private_key_file = File.dirname(__FILE__) + '/keys/encrypted_private'
|
14
|
+
|
15
|
+
@orig = 'sentry'
|
16
|
+
Sentry::AsymmetricSentry.default_public_key_file = @public_key_file
|
17
|
+
Sentry::AsymmetricSentry.default_private_key_file = @private_key_file
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_encrypt_creditcard
|
21
|
+
u = User.create :login => 'jones'
|
22
|
+
u.creditcard = @orig
|
23
|
+
assert u.save
|
24
|
+
assert !u.crypted_creditcard.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_decrypt_creditcard
|
28
|
+
assert_equal @orig, users(:user_1).creditcard
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_not_decrypt_encrypted_creditcard_with_invalid_key
|
32
|
+
assert_nil users(:user_2).creditcard
|
33
|
+
assert_nil users(:user_2).creditcard(@key)
|
34
|
+
use_encrypted_keys
|
35
|
+
assert_nil users(:user_1).creditcard
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_not_decrypt_encrypted_creditcard
|
39
|
+
use_encrypted_keys
|
40
|
+
assert_nil users(:user_2).creditcard
|
41
|
+
assert_nil users(:user_2).creditcard('other secret')
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_encrypt_encrypted_creditcard
|
45
|
+
use_encrypted_keys
|
46
|
+
u = User.create :login => 'jones'
|
47
|
+
u.creditcard = @orig
|
48
|
+
assert u.save
|
49
|
+
assert !u.crypted_creditcard.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_decrypt_encrypted_creditcard
|
53
|
+
use_encrypted_keys
|
54
|
+
assert_equal @orig, users(:user_2).creditcard(@key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def use_encrypted_keys
|
58
|
+
Sentry::AsymmetricSentry.default_public_key_file = @encrypted_public_key_file
|
59
|
+
Sentry::AsymmetricSentry.default_private_key_file = @encrypted_private_key_file
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class AsymmetricSentryTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@str = 'sentry'
|
6
|
+
@key = 'secret'
|
7
|
+
@public_key_file = File.dirname(__FILE__) + '/keys/public'
|
8
|
+
@private_key_file = File.dirname(__FILE__) + '/keys/private'
|
9
|
+
@encrypted_public_key_file = File.dirname(__FILE__) + '/keys/encrypted_public'
|
10
|
+
@encrypted_private_key_file = File.dirname(__FILE__) + '/keys/encrypted_private'
|
11
|
+
@sentry = Sentry::AsymmetricSentry.new
|
12
|
+
|
13
|
+
@orig = 'sentry'
|
14
|
+
@data = "vYfMxtVB8ezXmQKSNqTC9sPgi8TbsYRxWd7DVbpprzyuEdZ7gftJ/0IXsbXm\nXCU08bTAl0uEFm7dau+eJMXEJg==\n"
|
15
|
+
@encrypted_data = "q2obYAITmK93ylzVS01mJx1jSlnmylMX15nFpb4uKesVgnqvtzBRHZ/SK+Nm\nEzceIoAcJc3DHosVa4VUE/aK/A==\n"
|
16
|
+
Sentry::AsymmetricSentry.default_public_key_file = nil
|
17
|
+
Sentry::AsymmetricSentry.default_private_key_file = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_decrypt_files
|
21
|
+
set_key_files @public_key_file, @private_key_file
|
22
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@data)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_decrypt_files_with_encrypted_key
|
26
|
+
set_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
27
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_read_key_files
|
31
|
+
assert !@sentry.public?
|
32
|
+
assert !@sentry.private?
|
33
|
+
set_key_files @public_key_file, @private_key_file
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_read_encrypted_key_files
|
37
|
+
assert !@sentry.public?
|
38
|
+
assert !@sentry.private?
|
39
|
+
set_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_decrypt_files_with_default_key
|
43
|
+
set_default_key_files @public_key_file, @private_key_file
|
44
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@data)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_decrypt_files_with_default_encrypted_key
|
48
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
49
|
+
assert_equal @orig, @sentry.decrypt_from_base64(@encrypted_data, @key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_decrypt_files_with_default_key_using_class_method
|
53
|
+
set_default_key_files @public_key_file, @private_key_file
|
54
|
+
assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_should_decrypt_files_with_default_encrypted_key_using_class_method
|
58
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
59
|
+
assert_equal @orig, Sentry::AsymmetricSentry.decrypt_from_base64(@encrypted_data, @key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_read_key_files_with_default_key
|
63
|
+
assert !@sentry.public?
|
64
|
+
assert !@sentry.private?
|
65
|
+
set_default_key_files @public_key_file, @private_key_file
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_read_encrypted_key_files_with_default_key
|
69
|
+
assert !@sentry.public?
|
70
|
+
assert !@sentry.private?
|
71
|
+
set_default_key_files @encrypted_public_key_file, @encrypted_private_key_file
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def set_key_files(public_key, private_key)
|
76
|
+
@sentry.public_key_file = public_key
|
77
|
+
@sentry.private_key_file = private_key
|
78
|
+
assert @sentry.private?
|
79
|
+
assert @sentry.public?
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_default_key_files(public_key, private_key)
|
83
|
+
Sentry::AsymmetricSentry.default_public_key_file = public_key
|
84
|
+
Sentry::AsymmetricSentry.default_private_key_file = private_key
|
85
|
+
assert @sentry.private?
|
86
|
+
assert @sentry.public?
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
print "Using native DB2\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
db1 = 'arsentry'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => "db2",
|
10
|
+
:host => "localhost",
|
11
|
+
:username => "arunit",
|
12
|
+
:password => "arunit",
|
13
|
+
:database => db1
|
14
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
print "Using native MySQL\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
db1 = 'activerecord_sentry'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => "mysql",
|
10
|
+
:host => "localhost",
|
11
|
+
:username => "rails",
|
12
|
+
:password => "",
|
13
|
+
:database => db1
|
14
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
print "Using OCI Oracle\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new STDOUT
|
5
|
+
ActiveRecord::Base.logger.level = Logger::WARN
|
6
|
+
|
7
|
+
db1 = 'activerecord_sentry'
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
:adapter => 'oci',
|
11
|
+
:host => '', # can use an oracle SID
|
12
|
+
:username => 'arunit',
|
13
|
+
:password => 'arunit',
|
14
|
+
:database => db1
|
15
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
print "Using native PostgreSQL\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
db1 = 'activerecord_sentry'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => "postgresql",
|
10
|
+
:host => nil,
|
11
|
+
:username => "postgres",
|
12
|
+
:password => "postgres",
|
13
|
+
:database => db1
|
14
|
+
)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
print "Using native SQlite\n"
|
2
|
+
require 'logger'
|
3
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
4
|
+
|
5
|
+
class SqliteError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
BASE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../fixtures')
|
9
|
+
sqlite_test_db = "#{BASE_DIR}/activerecord_sentry.sqlite"
|
10
|
+
|
11
|
+
def make_connection(clazz, db_file, db_definitions_file)
|
12
|
+
unless File.exist?(db_file)
|
13
|
+
puts "SQLite database not found at #{db_file}. Rebuilding it."
|
14
|
+
sqlite_command = %Q{sqlite #{db_file} "create table a (a integer); drop table a;"}
|
15
|
+
puts "Executing '#{sqlite_command}'"
|
16
|
+
raise SqliteError.new("Seems that there is no sqlite executable available") unless system(sqlite_command)
|
17
|
+
clazz.establish_connection(
|
18
|
+
:adapter => "sqlite",
|
19
|
+
:dbfile => db_file)
|
20
|
+
script = File.read("#{BASE_DIR}/db_definitions/#{db_definitions_file}")
|
21
|
+
# SQLite-Ruby has problems with semi-colon separated commands, so split and execute one at a time
|
22
|
+
script.split(';').each do
|
23
|
+
|command|
|
24
|
+
clazz.connection.execute(command) unless command.strip.empty?
|
25
|
+
end
|
26
|
+
else
|
27
|
+
clazz.establish_connection(
|
28
|
+
:adapter => "sqlite",
|
29
|
+
:dbfile => db_file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
make_connection(ActiveRecord::Base, sqlite_test_db, 'sqlite.sql')
|
34
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
print "Using native SQLite3\n"
|
2
|
+
require 'logger'
|
3
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
4
|
+
|
5
|
+
class SqliteError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
BASE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../fixtures')
|
9
|
+
sqlite_test_db = "#{BASE_DIR}/activerecord_sentry.sqlite3"
|
10
|
+
|
11
|
+
def make_connection(clazz, db_file, db_definitions_file)
|
12
|
+
unless File.exist?(db_file)
|
13
|
+
puts "SQLite3 database not found at #{db_file}. Rebuilding it."
|
14
|
+
sqlite_command = %Q{sqlite3 #{db_file} "create table a (a integer); drop table a;"}
|
15
|
+
puts "Executing '#{sqlite_command}'"
|
16
|
+
raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
|
17
|
+
clazz.establish_connection(
|
18
|
+
:adapter => "sqlite3",
|
19
|
+
:dbfile => db_file)
|
20
|
+
script = File.read("#{BASE_DIR}/db_definitions/#{db_definitions_file}")
|
21
|
+
# SQLite-Ruby has problems with semi-colon separated commands, so split and execute one at a time
|
22
|
+
script.split(';').each do
|
23
|
+
|command|
|
24
|
+
clazz.connection.execute(command) unless command.strip.empty?
|
25
|
+
end
|
26
|
+
else
|
27
|
+
clazz.establish_connection(
|
28
|
+
:adapter => "sqlite3",
|
29
|
+
:dbfile => db_file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
make_connection(ActiveRecord::Base, sqlite_test_db, 'sqlite.sql')
|
@@ -0,0 +1,14 @@
|
|
1
|
+
print "Using native SQLServer\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
db1 = 'activerecord_sentry'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => "sqlserver",
|
10
|
+
:host => "localhost",
|
11
|
+
:username => "sa",
|
12
|
+
:password => "",
|
13
|
+
:database => db1
|
14
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
print "Using native SQLServer via ODBC\n"
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new("debug.log")
|
5
|
+
|
6
|
+
dsn1 = 'activerecord_sentry'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => "sqlserver",
|
10
|
+
:mode => "ODBC",
|
11
|
+
:host => "localhost",
|
12
|
+
:username => "sa",
|
13
|
+
:password => "",
|
14
|
+
:dsn => dsn1
|
15
|
+
)
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
drop table users;
|
@@ -0,0 +1 @@
|
|
1
|
+
DROP TABLE 'pages';
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
generates_crypted :creditcard, :mode => :asymmetric
|
3
|
+
|
4
|
+
def self.validates_password
|
5
|
+
validates_presence_of :crypted_password
|
6
|
+
validates_presence_of :password, :on => :create
|
7
|
+
validates_length_of :password, :in => 4..40
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ShaUser < User
|
12
|
+
validates_password
|
13
|
+
validates_confirmation_of :password
|
14
|
+
generates_crypted :password # sha is used by default
|
15
|
+
end
|
16
|
+
|
17
|
+
class DangerousUser < User # no password confirmation
|
18
|
+
# validates_password
|
19
|
+
generates_crypted :password
|
20
|
+
end
|
21
|
+
|
22
|
+
class SymmetricUser < User
|
23
|
+
validates_password
|
24
|
+
generates_crypted :password, :mode => :symmetric
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
user_1:
|
2
|
+
id: 1
|
3
|
+
login: bob
|
4
|
+
crypted_password: "0XlmUuNpE2k=\n"
|
5
|
+
crypted_creditcard: "vYfMxtVB8ezXmQKSNqTC9sPgi8TbsYRxWd7DVbpprzyuEdZ7gftJ/0IXsbXm\nXCU08bTAl0uEFm7dau+eJMXEJg==\n"
|
6
|
+
type: SymmetricUser
|
7
|
+
user_2:
|
8
|
+
id: 2
|
9
|
+
login: fred
|
10
|
+
crypted_creditcard: "q2obYAITmK93ylzVS01mJx1jSlnmylMX15nFpb4uKesVgnqvtzBRHZ/SK+Nm\nEzceIoAcJc3DHosVa4VUE/aK/A==\n"
|
11
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
OBNa1q8kbx8pyZZjIpr/pZV0oulE2czh5JlPW/13XsBvoz+A2zxA9gchhi6c
|
2
|
+
3yvfqgcZdojcsep+IiTqeg3gOPB2xNbedpP1lm+9tEfgdb9r1CLzRcURh7Hg
|
3
|
+
ufWgyEkS0lloz/YLy4hg9YDKetFNF9fnrk3xVwZPwFVuk4l/Unw1FTXLHsrq
|
4
|
+
KG27cR8mvNOow4bk4LVhk/avFSM85m3ITySEnyJsQQDzsI/RrWcQ7Js+8Ynv
|
5
|
+
esN51E/T0CYtkMEne2zSaD5qUTJlQ7Qtn4UUeZkpYjn4xQZPxw4OjL6zofg7
|
6
|
+
lsqElSv1/qP3QI8aKcQQklVsHRc5AgsxOFX4J6g6lo4kOGOwn0Ex8IRDfOej
|
7
|
+
pq4SUDh9IXz+6FBieQrObB/xEsKysVwRSzXre6ObHlPFsigg5ekFPyCv5ZTz
|
8
|
+
0iP8+xe/FJRrYdR3r3F5pRkOy0pw9EqlrLjmOx3/fgxhLq8FWmcSBbH3h3SG
|
9
|
+
GkJlfHNjF77FTJjnHKzRS+5VpdW4IHbsjL+NlI1z9Ol//czYvSGv85NdJvkq
|
10
|
+
PmH3o0+uYdwY5PeSMOPV21nJ3dwiKlm5IMFasL3C5yVJNVTVZTS7vWdcgZ4U
|
11
|
+
XfWQ9Y266ibbqXPluv4nxt1+kgjxmPbjPdYrlB5t7a2+unzT3oE3f4VGOG+k
|
12
|
+
YqFg0ErHN+fu
|
data/test/keys/private
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIBOwIBAAJBAL/xeY6aqFx6z1ThNOwgPgxv3tsonTlCj8VkN3Ikumg6SzBuLxlV
|
3
|
+
i9gFQZ7K9Pv9o/7+xUTYODqBpVhwgLBeu2cCAwEAAQJAHyjFMfg7Yp/xLndMzxRA
|
4
|
+
3mX+yJckRtpeWo31TktWE3syks1r9OrfmxKiStM9kFRubeBHTihZrW92TYkROLxh
|
5
|
+
uQIhAPuftVTJZFDNxeYDKIMIMqwR8KZgtuf25cv4pTxYwPqLAiEAw0gNwDJHBkvo
|
6
|
+
da4402pZNQmBA6qCSf0svDXqoEoaShUCIGBma340Oe6LJ0pb42Vv+pnZtazIWMq9
|
7
|
+
2IQwmn1oM2bJAiEAhgP869mVRIzzi091UCG79tn+4DU0FPLasI+P5VD1mcECIQDb
|
8
|
+
3ndvbPcElVvdJgabxyWJJsNtBBNZYPsuc6NrQyShOw==
|
9
|
+
-----END RSA PRIVATE KEY-----
|
data/test/keys/public
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
class ShaSentryTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Sentry::ShaSentry.salt = 'salt'
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_encrypt
|
10
|
+
assert_equal 'f438229716cab43569496f3a3630b3727524b81b', Sentry::ShaSentry.encrypt('test')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_encrypt_with_salt
|
14
|
+
Sentry::ShaSentry.salt = 'different salt'
|
15
|
+
assert_equal '18e3256d71529db8fa65b2eef24a69ddad7070f3', Sentry::ShaSentry.encrypt('test')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_encrypt_user_password
|
19
|
+
u = ShaUser.new :login => 'bob'
|
20
|
+
u.password = u.password_confirmation = 'test'
|
21
|
+
assert u.save
|
22
|
+
assert u.crypted_password = 'f438229716cab43569496f3a3630b3727524b81b'
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_encrypt_user_password_without_confirmation
|
26
|
+
u = DangerousUser.new :login => 'bob'
|
27
|
+
u.password = 'test'
|
28
|
+
assert u.save
|
29
|
+
assert u.crypted_password = 'f438229716cab43569496f3a3630b3727524b81b'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/user'
|
3
|
+
|
4
|
+
class SymmetricSentryCallbackTest < Test::Unit::TestCase
|
5
|
+
fixtures :users
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@str = 'sentry'
|
9
|
+
Sentry::SymmetricSentry.default_key = @key = 'secret'
|
10
|
+
@encrypted = "0XlmUuNpE2k=\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_encrypt_user_password
|
14
|
+
u = SymmetricUser.new :login => 'bob'
|
15
|
+
u.password = @str
|
16
|
+
assert u.save
|
17
|
+
assert_equal @encrypted, u.crypted_password
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_decrypted_user_password
|
21
|
+
assert_equal @str, users(:user_1).password
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_return_nil_on_invalid_key
|
25
|
+
Sentry::SymmetricSentry.default_key = 'other secret'
|
26
|
+
assert_nil users(:user_1).password
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_raise_error_on_invalid_key
|
30
|
+
Sentry::SymmetricSentry.default_key = 'other secret'
|
31
|
+
assert_raises(OpenSSL::CipherError) { users(:user_1).password! }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class SymmetricSentryTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@str = 'sentry'
|
6
|
+
@key = 'secret'
|
7
|
+
@encrypted = "0XlmUuNpE2k=\n"
|
8
|
+
@sentry = Sentry::SymmetricSentry.new
|
9
|
+
Sentry::SymmetricSentry.default_key = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_should_encrypt
|
13
|
+
assert_equal @encrypted, @sentry.encrypt_to_base64(@str, @key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_decrypt
|
17
|
+
assert_equal @str, @sentry.decrypt_from_base64(@encrypted, @key)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_encrypt_with_default_key
|
21
|
+
Sentry::SymmetricSentry.default_key = @key
|
22
|
+
assert_equal @encrypted, @sentry.encrypt_to_base64(@str)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_decrypt_with_default_key
|
26
|
+
Sentry::SymmetricSentry.default_key = @key
|
27
|
+
assert_equal @str, @sentry.decrypt_from_base64(@encrypted)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_raise_error_when_encrypt_with_no_key
|
31
|
+
assert_raises(Sentry::NoKeyError) { @sentry.encrypt_to_base64(@str) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_should_raise_error_when_decrypt_with_no_key
|
35
|
+
assert_raises(Sentry::NoKeyError) { @sentry.decrypt_from_base64(@str) }
|
36
|
+
end
|
37
|
+
end
|
data/test/tests.rb
ADDED
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: sentry
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.2.8
|
7
|
+
date: 2005-09-17
|
8
|
+
summary: Sentry provides painless encryption services with a wrapper around some OpenSSL classes
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: technoweenie@gmail.com
|
12
|
+
homepage: http://techno-weenie.net
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire: sentry
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
authors:
|
28
|
+
- Rick Olson
|
29
|
+
files:
|
30
|
+
- lib/active_record
|
31
|
+
- lib/sentry
|
32
|
+
- lib/sentry.rb
|
33
|
+
- lib/active_record/sentry.rb
|
34
|
+
- lib/sentry/asymmetric_sentry.rb
|
35
|
+
- lib/sentry/asymmetric_sentry_callback.rb
|
36
|
+
- lib/sentry/sha_sentry.rb
|
37
|
+
- lib/sentry/symmetric_sentry.rb
|
38
|
+
- lib/sentry/symmetric_sentry_callback.rb
|
39
|
+
- test/abstract_unit.rb
|
40
|
+
- test/asymmetric_sentry_callback_test.rb
|
41
|
+
- test/asymmetric_sentry_test.rb
|
42
|
+
- test/connections
|
43
|
+
- test/fixtures
|
44
|
+
- test/keys
|
45
|
+
- test/sha_sentry_test.rb
|
46
|
+
- test/symmetric_sentry_callback_test.rb
|
47
|
+
- test/symmetric_sentry_test.rb
|
48
|
+
- test/tests.rb
|
49
|
+
- test/connections/native_db2
|
50
|
+
- test/connections/native_mysql
|
51
|
+
- test/connections/native_oci
|
52
|
+
- test/connections/native_postgresql
|
53
|
+
- test/connections/native_sqlite
|
54
|
+
- test/connections/native_sqlite3
|
55
|
+
- test/connections/native_sqlserver
|
56
|
+
- test/connections/native_sqlserver_odbc
|
57
|
+
- test/connections/native_db2/connection.rb
|
58
|
+
- test/connections/native_mysql/connection.rb
|
59
|
+
- test/connections/native_oci/connection.rb
|
60
|
+
- test/connections/native_postgresql/connection.rb
|
61
|
+
- test/connections/native_sqlite/connection.rb
|
62
|
+
- test/connections/native_sqlite3/connection.rb
|
63
|
+
- test/connections/native_sqlserver/connection.rb
|
64
|
+
- test/connections/native_sqlserver_odbc/connection.rb
|
65
|
+
- test/fixtures/activerecord_sentry.sqlite
|
66
|
+
- test/fixtures/activerecord_sentry.sqlite3
|
67
|
+
- test/fixtures/db_definitions
|
68
|
+
- test/fixtures/user.rb
|
69
|
+
- test/fixtures/users.yml
|
70
|
+
- test/fixtures/db_definitions/postgresql.drop.sql
|
71
|
+
- test/fixtures/db_definitions/postgresql.sql
|
72
|
+
- test/fixtures/db_definitions/sqlite.drop.sql
|
73
|
+
- test/fixtures/db_definitions/sqlite.sql
|
74
|
+
- test/keys/encrypted_private
|
75
|
+
- test/keys/encrypted_public
|
76
|
+
- test/keys/private
|
77
|
+
- test/keys/public
|
78
|
+
- README
|
79
|
+
- MIT-LICENSE
|
80
|
+
- CHANGELOG
|
81
|
+
- RUNNING_UNIT_TESTS
|
82
|
+
test_files:
|
83
|
+
- test/tests.rb
|
84
|
+
rdoc_options: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
requirements: []
|
89
|
+
dependencies: []
|