loyal3-sentry 0.4.2

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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .idea/*
2
+ *.log
3
+
data/CHANGELOG ADDED
@@ -0,0 +1,58 @@
1
+ *0.3.1* (7 Jan 2006)
2
+
3
+ * removed useless breakpoint [Solomon White]
4
+
5
+ *0.3* (29 Oct 2005)
6
+
7
+ * added rake task for generating asymmetric keys
8
+ * Switch to migrations and schema for testing setup
9
+
10
+ *0.2.9* (18 Sep 2005)
11
+
12
+ * First RubyForge release.
13
+
14
+ *0.2.8* (17 Sep 2005)
15
+
16
+ * Added Active Record unit tests
17
+
18
+ *0.2.7* (17 Sep 2005)
19
+
20
+ * Added rdocs and stubs for AR unit tests
21
+
22
+ *0.2.6* (2 Aug 2005)
23
+
24
+ * Fixed generates_crypted so it adds attribute accessors
25
+
26
+ *0.2.5* (27 Jul 2005)
27
+
28
+ * Set ActiveRecord callback objects to only encrypt fields when they are not empty.
29
+
30
+ *0.2.4* (11 Jul 2005)
31
+
32
+ * Split ActiveRecord callback methods into their own classes.
33
+ * Set AR virtual columns to fail silently on errors.
34
+
35
+ *0.2.3* (11 Jul 2005)
36
+
37
+ * Added ActiveRecord callback objects for SymmetricSentry and AsymmetricSentry. +one_way_encrypt+ is depreciated.
38
+ * Readme doc added too
39
+
40
+ *0.2.1* (9 Jul 2005)
41
+
42
+ * vastly simplified one_way_encrypt at danp's suggestion. Use this in your model to try it out:
43
+
44
+ +one_way_encrypt :password*
45
+
46
+ That generates an SHA hash of model.password to model.crypted_password which is saved in the DB.
47
+ model.password is a virtual field. Continue using validates_confirmation_of for confirmation.
48
+
49
+
50
+ *0.2* (9 Jul 2005)
51
+
52
+ * added ActiveRecord::Base#one_way_encrypt class method to hash passwords with SHA
53
+ * Renamed core classes to SymmetricSentry and AsymmetricSentry
54
+ * Test Suite added
55
+
56
+ *0.1*
57
+
58
+ * 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,94 @@
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
+ == Resources
7
+
8
+ Install
9
+
10
+ * gem install sentry
11
+
12
+ Rubyforge project
13
+
14
+ * http://rubyforge.org/projects/sentry
15
+
16
+ RDocs
17
+
18
+ * http://sentry.rubyforge.org
19
+
20
+ Subversion
21
+
22
+ * http://techno-weenie.net/svn/projects/sentry
23
+
24
+ Collaboa
25
+
26
+ * http://collaboa.techno-weenie.net/repository/browse/sentry
27
+
28
+ == Using with ActiveRecord
29
+
30
+ I wrote this for the purpose of encrypting ActiveRecord attributes. Just <tt>require 'sentry'</tt>, and some new
31
+ class methods will be available to you:
32
+
33
+ === generates_crypted
34
+
35
+ generates_crypted :password, :mode => :sha | :symmetric | :asymmetric
36
+
37
+ This is the generic class method to use. Default mode is :sha.
38
+
39
+ === generates_crypted_hash_of
40
+
41
+ generates_crypted_hash_of :password
42
+
43
+ This is a shortcut for using SHA encryption. No different than specifying <tt>generates_crypted :password</tt>. In the above
44
+ example, model.password is a virtual field, and the SHA hash is saved to model.crypted_password
45
+
46
+ === asymmetrically_encrypts
47
+
48
+ asymmetrically_encrypts :password
49
+
50
+ This is a shortcut for using an asymmetrical algorithm with a private/public key file. To use this, generate a public and
51
+ private key with Sentry::AsymmetricalSentry.save_random_rsa_key(private_key_file, public_key_file). If you want to encrypt the
52
+ private key file with a symmetrical algorithm, pass a secret key (neither the key nor the decrypted value will be stored).
53
+
54
+ Sentry::AsymmetricSentry.save_random_rsa_key(private_key_file, public_key_file, :key => 'secret_password')
55
+
56
+ What that does, is requires you to pass in that same secret password when accesing the method.
57
+
58
+ class Model < ActiveRecord::Base
59
+ generates_crypted :password, :mode => :asymmetric
60
+ end
61
+
62
+ model.password = '5234523453425'
63
+ model.save # password is encrypted and saved to crypted_password in the database,
64
+ # model.password is cleared and becomes a virtual field.
65
+ model.password('secret_password')
66
+ => '5234523453425'
67
+
68
+ The public and private key file names can be set in config/environment.rb
69
+
70
+ Sentry::AsymmetricSentry.default_public_key_file = "#{RAILS_ROOT}/config/public.key"
71
+ Sentry::AsymmetricSentry.default_private_key_file = "#{RAILS_ROOT}/config/private.key"
72
+
73
+ If the private key was encrypted with the Sentry::AsymmetricalSentry#save_random_rsa_key, you must provide that same key
74
+ when accessing the AR model.
75
+
76
+ === symmetrically_encrypts
77
+
78
+ symmetrically_encrypts :password
79
+
80
+ This is a shortcut for using a symmetrical algorithm with a secret password to encrypt the field.
81
+
82
+ class Model < ActiveRecord::Base
83
+ generates_crypted :password, :mode => :symmetric
84
+ end
85
+
86
+ model.password = '5234523453425'
87
+ model.save # password is encrypted and saved to crypted_password in the database,
88
+ # model.password is cleared and becomes a virtual field.
89
+ model.password
90
+ => '5234523453425'
91
+
92
+ The secret password can be set in config/environment.rb
93
+
94
+ Sentry::SymmetricSentry.default_key = "secret_password"
@@ -0,0 +1,42 @@
1
+ == Creating the test database
2
+
3
+ The default name for the test databases is "sentry_plugin_test". 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/database.yml.
6
+
7
+ Make sure that you create database objects with the same user that you specified in i
8
+ database.yml otherwise (on Postgres, at least) tests for default values will fail.
9
+
10
+ == Running with Rake
11
+
12
+ The easiest way to run the unit tests is through Rake. The default task runs
13
+ the entire test suite for the sqlite adapter. You can also run the suite on just
14
+ one adapter by passing the DB environment variable.
15
+
16
+ rake test DB=mysql
17
+
18
+ For more information, checkout the full array of rake tasks with "rake -T"
19
+
20
+ Rake can be found at http://rake.rubyforge.org
21
+
22
+ == Running by hand
23
+
24
+ Unit tests are located in test directory. If you only want to run a single test suite,
25
+ or don't want to bother with Rake, you can do so with something like:
26
+
27
+ cd test; DB=mysql ruby base_test.rb
28
+
29
+ That'll run the base suite using the MySQL adapter. Change the adapter
30
+ and test suite name as needed.
31
+
32
+ == Faster tests
33
+
34
+ If you are using a database that supports transactions, you can set the
35
+ "AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
36
+ This gives a very large speed boost. With rake:
37
+
38
+ rake AR_TX_FIXTURES=yes
39
+
40
+ Or, by hand:
41
+
42
+ AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
data/Rakefile ADDED
@@ -0,0 +1,192 @@
1
+ require 'rubygems'
2
+
3
+ #Gem::manage_gems
4
+
5
+ require 'rake/rdoctask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'rake/testtask'
9
+ require 'rake/contrib/rubyforgepublisher'
10
+
11
+ PKG_NAME = 'sentry'
12
+ PKG_VERSION = '0.3.1'
13
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
14
+ PROD_HOST = "technoweenie@bidwell.textdrive.com"
15
+ RUBY_FORGE_PROJECT = 'sentry'
16
+ RUBY_FORGE_USER = 'technoweenie'
17
+
18
+ task :default => [:test]
19
+ Rake::TestTask.new("test") do |t|
20
+ t.libs << "test"
21
+ t.pattern = "test/*_test.rb"
22
+ t.verbose = true
23
+ end
24
+
25
+ load 'tasks/sentry.rake'
26
+
27
+ Rake::RDocTask.new do |rdoc|
28
+ rdoc.rdoc_dir = 'doc'
29
+ rdoc.title = "#{PKG_NAME} -- painless encryption for Active Record"
30
+ rdoc.options << '--line-numbers --inline-source --accessor cattr_accessor=object'
31
+ rdoc.template = "#{ENV['template']}.rb" if ENV['template']
32
+ rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS')
33
+ rdoc.rdoc_files.include('lib/**/*.rb')
34
+ end
35
+
36
+ spec = Gem::Specification.new do |s|
37
+ s.name = PKG_NAME
38
+ s.version = PKG_VERSION
39
+ s.platform = Gem::Platform::RUBY
40
+ s.summary = "Sentry provides painless encryption services with a wrapper around some OpenSSL classes"
41
+ s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS)
42
+ s.files.delete "test/sentry_plugin.sqlite.db"
43
+ s.files.delete "test/sentry_plugin.sqlite3.db"
44
+ s.require_path = 'lib'
45
+ s.autorequire = 'sentry'
46
+ s.has_rdoc = true
47
+ s.test_file = 'test/tests.rb'
48
+ s.author = "Rick Olson"
49
+ s.email = "technoweenie@gmail.com"
50
+ s.homepage = "http://techno-weenie.net"
51
+ end
52
+
53
+ Rake::GemPackageTask.new(spec) do |pkg|
54
+ pkg.need_tar = true
55
+ end
56
+
57
+ desc "Publish the API documentation"
58
+ task :pdoc => [:rdoc] do
59
+ Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
60
+ end
61
+
62
+ desc 'Publish the gem and API docs'
63
+ task :publish => [:pdoc, :rubyforge_upload]
64
+
65
+ desc "Publish the release files to RubyForge."
66
+ task :rubyforge_upload => :package do
67
+ files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
68
+
69
+ if RUBY_FORGE_PROJECT then
70
+ require 'net/http'
71
+ require 'open-uri'
72
+
73
+ project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
74
+ project_data = open(project_uri) { |data| data.read }
75
+ group_id = project_data[/[?&]group_id=(\d+)/, 1]
76
+ raise "Couldn't get group id" unless group_id
77
+
78
+ # This echos password to shell which is a bit sucky
79
+ if ENV["RUBY_FORGE_PASSWORD"]
80
+ password = ENV["RUBY_FORGE_PASSWORD"]
81
+ else
82
+ print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
83
+ password = STDIN.gets.chomp
84
+ end
85
+
86
+ login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
87
+ data = [
88
+ "login=1",
89
+ "form_loginname=#{RUBY_FORGE_USER}",
90
+ "form_pw=#{password}"
91
+ ].join("&")
92
+ http.post("/account/login.php", data)
93
+ end
94
+
95
+ cookie = login_response["set-cookie"]
96
+ raise "Login failed" unless cookie
97
+ headers = { "Cookie" => cookie }
98
+
99
+ release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
100
+ release_data = open(release_uri, headers) { |data| data.read }
101
+ package_id = release_data[/[?&]package_id=(\d+)/, 1]
102
+ raise "Couldn't get package id" unless package_id
103
+
104
+ first_file = true
105
+ release_id = ""
106
+
107
+ files.each do |filename|
108
+ basename = File.basename(filename)
109
+ file_ext = File.extname(filename)
110
+ file_data = File.open(filename, "rb") { |file| file.read }
111
+
112
+ puts "Releasing #{basename}..."
113
+
114
+ release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
115
+ release_date = Time.now.strftime("%Y-%m-%d %H:%M")
116
+ type_map = {
117
+ ".zip" => "3000",
118
+ ".tgz" => "3110",
119
+ ".gz" => "3110",
120
+ ".gem" => "1400"
121
+ }; type_map.default = "9999"
122
+ type = type_map[file_ext]
123
+ boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
124
+
125
+ query_hash = if first_file then
126
+ {
127
+ "group_id" => group_id,
128
+ "package_id" => package_id,
129
+ "release_name" => PKG_FILE_NAME,
130
+ "release_date" => release_date,
131
+ "type_id" => type,
132
+ "processor_id" => "8000", # Any
133
+ "release_notes" => "",
134
+ "release_changes" => "",
135
+ "preformatted" => "1",
136
+ "submit" => "1"
137
+ }
138
+ else
139
+ {
140
+ "group_id" => group_id,
141
+ "release_id" => release_id,
142
+ "package_id" => package_id,
143
+ "step2" => "1",
144
+ "type_id" => type,
145
+ "processor_id" => "8000", # Any
146
+ "submit" => "Add This File"
147
+ }
148
+ end
149
+
150
+ query = "?" + query_hash.map do |(name, value)|
151
+ [name, URI.encode(value)].join("=")
152
+ end.join("&")
153
+
154
+ data = [
155
+ "--" + boundary,
156
+ "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
157
+ "Content-Type: application/octet-stream",
158
+ "Content-Transfer-Encoding: binary",
159
+ "", file_data, ""
160
+ ].join("\x0D\x0A")
161
+
162
+ release_headers = headers.merge(
163
+ "Content-Type" => "multipart/form-data; boundary=#{boundary}"
164
+ )
165
+
166
+ target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
167
+ http.post(target + query, data, release_headers)
168
+ end
169
+
170
+ if first_file then
171
+ release_id = release_response.body[/release_id=(\d+)/, 1]
172
+ raise("Couldn't get release id") unless release_id
173
+ end
174
+
175
+ first_file = false
176
+ end
177
+ end
178
+ end
179
+
180
+ begin
181
+ require 'jeweler'
182
+ Jeweler::Tasks.new do |gemspec|
183
+ gemspec.name = "sentry"
184
+ gemspec.summary = "Asymmetric encryption of active record fields"
185
+ gemspec.description = "Asymmetric encryption of active record fields"
186
+ gemspec.email = "commoncode@pivotallabs.com"
187
+ gemspec.homepage = "http://github.com/pivotal/sentry"
188
+ gemspec.authors = ["John Pelly", "David Stevenson"]
189
+ end
190
+ rescue LoadError
191
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
192
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.2
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'sentry'
@@ -0,0 +1,96 @@
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] || :asymmetric
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, options = {})
26
+ #temp_sentry = ::Sentry::AsymmetricSentryCallback.new(attr_name)
27
+ #before_validation temp_sentry
28
+ #after_save temp_sentry
29
+ unless instance_methods.include?("#{attr_name}_with_decryption")
30
+ define_read_methods
31
+
32
+ define_method("#{attr_name}_with_decryption") do |*optional|
33
+ begin
34
+ crypted_value = self.send("#{attr_name}_without_decryption")
35
+ return nil if crypted_value.nil?
36
+ key = optional.shift || (options[:key].is_a?(Proc) ? options[:key].call : options[:key]) || ::Sentry.default_key
37
+ decrypted_value = ::Sentry::AsymmetricSentry.decrypt_from_base64(crypted_value, key)
38
+ return decrypted_value[8,decrypted_value.length-8]
39
+ rescue
40
+ nil
41
+ end
42
+ end
43
+
44
+ alias_method_chain attr_name, :decryption
45
+ alias_method "crypted_#{attr_name}", "#{attr_name}_without_decryption"
46
+ alias_method "#{attr_name}_before_type_cast", "#{attr_name}_with_decryption"
47
+
48
+ define_method("#{attr_name}_with_encryption=") do |value|
49
+ padded_value = ActiveRecord::Sentry.rand_string + value
50
+ encrypted_value = ::Sentry::AsymmetricSentry.encrypt_to_base64(padded_value)
51
+ self.send("#{attr_name}_without_encryption=", encrypted_value)
52
+ nil
53
+ end
54
+
55
+ alias_method_chain "#{attr_name}=", :encryption
56
+ end
57
+
58
+ end
59
+ private
60
+
61
+ #def symmetrically_encrypts(attr_name)
62
+ # temp_sentry = ::Sentry::SymmetricSentryCallback.new(attr_name)
63
+ # before_validation temp_sentry
64
+ # after_save temp_sentry
65
+ #
66
+ # define_method(attr_name) do
67
+ # send("#{attr_name}!") rescue nil
68
+ # end
69
+ #
70
+ # define_method("#{attr_name}!") do
71
+ # return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
72
+ # return nil if send("crypted_#{attr_name}").nil?
73
+ # ::Sentry::SymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"))
74
+ # end
75
+ #
76
+ # define_method("#{attr_name}=") do |value|
77
+ # decrypted_values[attr_name] = value
78
+ # nil
79
+ # end
80
+ #
81
+ # private
82
+ # define_method(:decrypted_values) do
83
+ # @decrypted_values ||= {}
84
+ # end
85
+ #end
86
+ end
87
+
88
+ @@CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
89
+
90
+ def self.rand_string(length=8)
91
+ s=''
92
+ length.times{ s << @@CHARS[rand(@@CHARS.length)] }
93
+ s
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,146 @@
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
+ rsa = public_rsa
24
+ rsa.public_encrypt(data)
25
+ end
26
+
27
+ def encrypt_to_base64(data)
28
+ Base64.encode64(encrypt(data))
29
+ end
30
+
31
+ def decrypt(data, key = nil)
32
+ raise NoPrivateKeyError unless private?
33
+ rsa = private_rsa(key)
34
+ rsa.private_decrypt(data)
35
+ end
36
+
37
+ def decrypt_from_base64(data, key = nil)
38
+ decrypt(Base64.decode64(data), key)
39
+ end
40
+
41
+ def private_key_file=(file)
42
+ @private_key_file = file and load_private_key
43
+ end
44
+
45
+ def public_key_file=(file)
46
+ @public_key_file = file and load_public_key
47
+ end
48
+
49
+ def public?
50
+ return true unless @public_key.nil?
51
+ load_public_key and return @public_key
52
+ end
53
+
54
+ def private?
55
+ return true unless @private_key.nil?
56
+ load_private_key and return @private_key
57
+ end
58
+
59
+ class << self
60
+ # * <tt>:key</tt> - secret password
61
+ # * <tt>:symmetric_algorithm</tt> - symmetrical algorithm to use
62
+ def save_random_rsa_key(private_key_file, public_key_file, options = {})
63
+ rsa = OpenSSL::PKey::RSA.new(512)
64
+ public_key = rsa.public_key
65
+ private_key = options[:key].to_s.empty? ?
66
+ rsa.to_s :
67
+ SymmetricSentry.new(:algorithm => options[:symmetric_algorithm]).encrypt_to_base64(rsa.to_s, options[:key])
68
+ File.open(public_key_file, 'w') { |f| f.write(public_key) }
69
+ File.open(private_key_file, 'w') { |f| f.write(private_key) }
70
+ end
71
+
72
+ def encrypt(data)
73
+ self.new.encrypt(data)
74
+ end
75
+
76
+ def encrypt_to_base64(data)
77
+ self.new.encrypt_to_base64(data)
78
+ end
79
+
80
+ def decrypt(data, key = nil)
81
+ self.new.decrypt(data, key)
82
+ end
83
+
84
+ def decrypt_from_base64(data, key = nil)
85
+ self.new.decrypt_from_base64(data, key)
86
+ end
87
+
88
+ # cattr_accessor would be lovely
89
+ def default_private_key_file
90
+ @@default_private_key_file
91
+ end
92
+
93
+ def default_private_key_file=(value)
94
+ @@default_private_key_file = value
95
+ end
96
+
97
+ def default_public_key_file
98
+ @@default_public_key_file
99
+ end
100
+
101
+ def default_public_key_file=(value)
102
+ @@default_public_key_file = value
103
+ end
104
+
105
+ def default_symmetric_algorithm
106
+ @@default_symmetric_algorithm
107
+ end
108
+
109
+ def default_symmetric_algorithm=(value)
110
+ @@default_symmetric_algorithm = value
111
+ end
112
+ end
113
+
114
+ private
115
+ def encryptor
116
+ @encryptor ||= SymmetricSentry.new(:algorithm => @symmetric_algorithm)
117
+ end
118
+
119
+ def load_private_key
120
+ @private_rsa = nil
121
+ @private_key_file ||= @@default_private_key_file
122
+ if @private_key_file and File.file?(@private_key_file)
123
+ @private_key = File.open(@private_key_file) { |f| f.read }
124
+ end
125
+ end
126
+
127
+ def load_public_key
128
+ @public_rsa = nil
129
+ @public_key_file ||= @@default_public_key_file
130
+ if @public_key_file and File.file?(@public_key_file)
131
+ @public_key = File.open(@public_key_file) { |f| f.read }
132
+ end
133
+ end
134
+
135
+ # retrieves private rsa from encrypted private key
136
+ def private_rsa(key = nil)
137
+ return @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) unless key
138
+ OpenSSL::PKey::RSA.new(encryptor.decrypt_from_base64(@private_key, key))
139
+ end
140
+
141
+ # retrieves public rsa
142
+ def public_rsa
143
+ @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
144
+ end
145
+ end
146
+ 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