stderr-sentry 0.5.4

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/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.3
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'sentry'
@@ -0,0 +1,105 @@
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
+ #puts "crypted value: #{crypted_value}"
36
+ return nil if crypted_value.nil?
37
+ key = optional.shift || (options[:key].is_a?(Proc) ? options[:key].call : options[:key]) || ::Sentry.default_key
38
+ decrypted_value = ::Sentry::AsymmetricSentry.decrypt_large_from_base64(crypted_value, key)
39
+ return decrypted_value
40
+ rescue Exception => e
41
+ nil
42
+ end
43
+ end
44
+
45
+ alias_method_chain attr_name, :decryption
46
+ alias_method "crypted_#{attr_name}", "#{attr_name}_without_decryption"
47
+ alias_method "#{attr_name}_before_type_cast", "#{attr_name}_with_decryption"
48
+
49
+ define_method("#{attr_name}_with_encryption=") do |value|
50
+ encrypted_value = self.class.encrypt_for_sentry(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
+
60
+ def encrypt_for_sentry(string)
61
+ return nil if string.nil?
62
+ return ::Sentry::AsymmetricSentry.encrypt_large_to_base64(string)
63
+ end
64
+
65
+ private
66
+
67
+ def symmetrically_encrypts(attr_name)
68
+ temp_sentry = ::Sentry::SymmetricSentryCallback.new(attr_name)
69
+ before_validation temp_sentry
70
+ after_save temp_sentry
71
+
72
+ define_method("#{attr_name}=") do |value|
73
+ decrypted_values[attr_name] = value
74
+ nil
75
+ end
76
+
77
+ define_method(attr_name) do
78
+ send("#{attr_name}!") rescue nil
79
+ end
80
+
81
+ define_method("#{attr_name}!") do
82
+ return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
83
+
84
+ return nil if send("crypted_#{attr_name}").nil?
85
+ ::Sentry::SymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"))
86
+ end
87
+
88
+
89
+
90
+ private
91
+ define_method(:decrypted_values) do
92
+ @decrypted_values ||= {}
93
+ end
94
+ end
95
+ end
96
+
97
+ @@CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
98
+
99
+ def self.rand_string(length=8)
100
+ s=''
101
+ length.times{ s << @@CHARS[rand(@@CHARS.length)] }
102
+ s
103
+ end
104
+ end
105
+ end
data/lib/sentry.rb ADDED
@@ -0,0 +1,71 @@
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
+ mattr_accessor :default_key
38
+ end
39
+
40
+ begin
41
+ require 'active_record/sentry'
42
+ ActiveRecord::Base.class_eval do
43
+ include ActiveRecord::Sentry
44
+ end
45
+ rescue NameError
46
+ nil
47
+ end
48
+
49
+ class OpenSSL::PKey::RSA
50
+ def max_encryptable_length
51
+ @max_encryption_length ||= calc_max_encrypted_length
52
+ end
53
+
54
+ private
55
+
56
+ def calc_max_encrypted_length
57
+ upper_bound = 4*1024
58
+ test_length = upper_bound / 2
59
+ while test_length != (upper_bound - 1)
60
+ probe = "a" * test_length
61
+ begin
62
+ self.public_encrypt(probe)
63
+ test_length = test_length + ((upper_bound - test_length) / 2)
64
+ rescue Exception => e
65
+ upper_bound = test_length
66
+ test_length = test_length / 2
67
+ end
68
+ end
69
+ return test_length
70
+ end
71
+ end
@@ -0,0 +1,192 @@
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
+ self.private_key_file = options[:private_key_file]
17
+ self.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
+ return rsa.public_encrypt(data)
25
+ end
26
+
27
+ def decrypt_large_from_base64(data, key=nil)
28
+ raise NoPrivateKeyError unless private?
29
+ chunk_length = public_rsa.max_encryptable_length + 11 # 11 is magic padding for RSA encoding
30
+ b64_decoded = Base64.decode64(data)
31
+ padding_length = b64_decoded[0]
32
+ data = b64_decoded[1, data.length]
33
+ return (0...data.length).step(chunk_length).inject("") { |accum, idx| accum + decrypt_with_padding(data.slice(idx, chunk_length), padding_length, key)}
34
+ end
35
+
36
+ def chunk_size(padding_length)
37
+ return public_rsa.max_encryptable_length - padding_length
38
+ end
39
+
40
+ def encrypt_large_to_base64(data)
41
+ raise NoPublicKeyError unless public?
42
+ padding_length = 8
43
+ chunk_length = chunk_size(padding_length)
44
+ return Base64.encode64(padding_length.chr + (0...data.length).step(chunk_length).inject("") {|accum, idx| accum + encrypt_with_padding( data.slice(idx, chunk_length), padding_length)} )
45
+ end
46
+
47
+ def decrypt_with_padding(data, padding_length, key=nil)
48
+ decrypted = decrypt(data, key)
49
+ return decrypted[0, decrypted.length - padding_length]
50
+ end
51
+
52
+ def encrypt_with_padding(data, padding_length)
53
+ encrypt(data + rand_string(padding_length))
54
+ end
55
+
56
+ @@CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
57
+
58
+ def rand_string(length=8)
59
+ s=''
60
+ length.times{ s << @@CHARS[rand(@@CHARS.length)] }
61
+ s
62
+ end
63
+
64
+ def encrypt_to_base64(data)
65
+ Base64.encode64(encrypt(data))
66
+ end
67
+
68
+ def decrypt(data, key = nil)
69
+ raise NoPrivateKeyError unless private?
70
+ rsa = private_rsa(key)
71
+ return rsa.private_decrypt(data)
72
+ end
73
+
74
+ def decrypt_from_base64(data, key = nil)
75
+ decrypt(Base64.decode64(data), key)
76
+ end
77
+
78
+ def private_key_file=(file)
79
+ @private_key_file = file and load_private_key
80
+ end
81
+
82
+ def public_key_file=(file)
83
+ @public_key_file = file and load_public_key
84
+ end
85
+
86
+ def public?
87
+ return true unless @public_key.nil?
88
+ load_public_key and return @public_key
89
+ end
90
+
91
+ def private?
92
+ return true unless @private_key.nil?
93
+ load_private_key and return @private_key
94
+ end
95
+
96
+ class << self
97
+ # * <tt>:key</tt> - secret password
98
+ # * <tt>:symmetric_algorithm</tt> - symmetrical algorithm to use
99
+ def save_random_rsa_key(private_key_file, public_key_file, options = {})
100
+ rsa = OpenSSL::PKey::RSA.new(512)
101
+ public_key = rsa.public_key
102
+ private_key = options[:key].to_s.empty? ?
103
+ rsa.to_s :
104
+ SymmetricSentry.new(:algorithm => options[:symmetric_algorithm]).encrypt_to_base64(rsa.to_s, options[:key])
105
+ File.open(public_key_file, 'w') { |f| f.write(public_key) }
106
+ File.open(private_key_file, 'w') { |f| f.write(private_key) }
107
+ end
108
+
109
+ def encrypt(data)
110
+ self.new.encrypt(data)
111
+ end
112
+
113
+ def encrypt_to_base64(data)
114
+ self.new.encrypt_to_base64(data)
115
+ end
116
+
117
+ def encrypt_large_to_base64(data)
118
+ self.new.encrypt_large_to_base64(data)
119
+ end
120
+
121
+ def decrypt(data, key = nil)
122
+ self.new.decrypt(data, key)
123
+ end
124
+
125
+ def decrypt_large_from_base64(data, key = nil)
126
+ self.new.decrypt_large_from_base64(data, key)
127
+ end
128
+
129
+ def decrypt_from_base64(data, key = nil)
130
+ self.new.decrypt_from_base64(data, key)
131
+ end
132
+
133
+ # cattr_accessor would be lovely
134
+ def default_private_key_file
135
+ @@default_private_key_file
136
+ end
137
+
138
+ def default_private_key_file=(value)
139
+ @@default_private_key_file = value
140
+ end
141
+
142
+ def default_public_key_file
143
+ @@default_public_key_file
144
+ end
145
+
146
+ def default_public_key_file=(value)
147
+ @@default_public_key_file = value
148
+ end
149
+
150
+ def default_symmetric_algorithm
151
+ @@default_symmetric_algorithm
152
+ end
153
+
154
+ def default_symmetric_algorithm=(value)
155
+ @@default_symmetric_algorithm = value
156
+ end
157
+ end
158
+
159
+ private
160
+ def encryptor
161
+ @encryptor ||= SymmetricSentry.new(:algorithm => @symmetric_algorithm)
162
+ end
163
+
164
+ def load_private_key
165
+ @private_rsa = nil
166
+ @private_key_file ||= @@default_private_key_file
167
+ if @private_key_file and File.file?(@private_key_file)
168
+ @private_key = File.open(@private_key_file) { |f| f.read }
169
+ end
170
+ return @private_key
171
+ end
172
+
173
+ def load_public_key
174
+ @public_rsa = nil
175
+ @public_key_file ||= @@default_public_key_file
176
+ if @public_key_file and File.file?(@public_key_file)
177
+ @public_key = File.open(@public_key_file) { |f| f.read }
178
+ end
179
+ end
180
+
181
+ # retrieves private rsa from encrypted private key
182
+ def private_rsa(key = nil)
183
+ return @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) unless key
184
+ OpenSSL::PKey::RSA.new(encryptor.decrypt_from_base64(@private_key, key))
185
+ end
186
+
187
+ # retrieves public rsa
188
+ def public_rsa
189
+ @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
190
+ end
191
+ end
192
+ 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,82 @@
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
14
+ des.pkcs5_keyivgen(key)
15
+ cipher = des.update(data)
16
+
17
+ cipher << des.final
18
+ end
19
+
20
+ def encrypt_to_base64(text, key = nil)
21
+ Base64.encode64(encrypt(text, key))
22
+ end
23
+
24
+ def decrypt(data, key = nil)
25
+ key = check_for_key!(key)
26
+ des = encryptor
27
+ des.decrypt
28
+ des.pkcs5_keyivgen(key)
29
+ text = des.update(data)
30
+ text << des.final
31
+ end
32
+
33
+ def decrypt_from_base64(text, key = nil)
34
+ decrypt(Base64.decode64(text), key)
35
+ end
36
+
37
+ class << self
38
+ def default_algorithm
39
+ @@default_algorithm
40
+ end
41
+
42
+ def default_algorithm=(value)
43
+ @@default_algorithm = value
44
+ end
45
+
46
+ def default_key
47
+ @@default_key
48
+ end
49
+
50
+ def default_key=(value)
51
+ @@default_key = value
52
+ end
53
+
54
+ def encrypt(data, key = nil)
55
+ self.new.encrypt(data, key)
56
+ end
57
+
58
+ def encrypt_to_base64(text, key = nil)
59
+ self.new.encrypt_to_base64(text, key)
60
+ end
61
+
62
+ def decrypt(data, key = nil)
63
+ self.new.decrypt(data, key)
64
+ end
65
+
66
+ def decrypt_from_base64(text, key = nil)
67
+ self.new.decrypt_from_base64(text, key)
68
+ end
69
+ end
70
+
71
+ private
72
+ def encryptor
73
+ @encryptor ||= OpenSSL::Cipher::Cipher.new(@algorithm)
74
+ end
75
+
76
+ def check_for_key!(key)
77
+ valid_key = key || @@default_key
78
+ raise Sentry::NoKeyError if valid_key.nil?
79
+ valid_key
80
+ end
81
+ end
82
+ end