quantipay-acts_as_secure 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/CHANGELOG +34 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +149 -0
  4. data/Rakefile +35 -0
  5. data/init.rb +1 -0
  6. data/lib/acts_as_secure.rb +133 -0
  7. metadata +62 -0
data/CHANGELOG ADDED
@@ -0,0 +1,34 @@
1
+ rel_1_0_1:
2
+ date: 2009-12-20
3
+ notes:
4
+ - Fixed AR serialization - now adds "serialize" to the model when adding callbacks
5
+ -- Added secure_column_symbols to return an array of columns to be secured, as symbols. Not very DRY, but works for now
6
+ - Defaulting to :text fields instead of binary for :storage type. To allow storing base64 encoded encrypted data
7
+ - Need to verify :only and :except returns a valid set of columns to secure - haven't tested this much at the moment.
8
+
9
+ rel_1_0_0:
10
+ date: 2009-12-20
11
+ notes:
12
+ - Updates to use AR serialization to manage storage of data to the DB
13
+ - Added acts_as_secure :only => [] option to allow specifying specific columns to be secured
14
+ - Added update_pk_password class method to allow setting the private key password
15
+ - Added gemspec
16
+ rel_0_0_4:
17
+ date: 2008-09-06
18
+ notes:
19
+ - No decryption for NULL fields
20
+
21
+ rel_0_0_3:
22
+ date: 2007-06-26
23
+ notes:
24
+ - Added support for nested crypto blocks
25
+
26
+ rel_0_0_2:
27
+ date: 2007-06-18
28
+ notes:
29
+ - Added support for model inheritance
30
+
31
+ rel_0_0_1:
32
+ date: 2005-04-26
33
+ notes:
34
+ - Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Revolution Health Group LLC. All rights reserved.
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,149 @@
1
+ == Introduction
2
+
3
+ ActsAsSecure adds an ability to store ActiveRecord model's fields encrypted in a DB. When a model is marked with acts_as_secure, the :binary type fields are recognized as needed to be stored encrypted. The plugin does before_save/after_save/after_find encryption/decryption thus making it transparent for a code using the secure models.
4
+
5
+ The plugin supports a master key approach as well as individual records encryption keys. It does not contain any crypto provider but allows to plug in any external one as long as it supports encrypt/decrypt methods.
6
+
7
+ The fields are converted to a YAML form before encryption. After description they are restored via YAML.load. Since fields are stored encrypted, the find usage is very limited.
8
+
9
+ == Usage
10
+
11
+ === Master Key Provider Usage
12
+
13
+ class SecureModel < ActiveRecord::Base
14
+ acts_as_secure :crypto_provider => MasterKeyProviderClassOrInstance
15
+ end
16
+
17
+ SecureModel.create()
18
+ SecureModel.find(:first)
19
+
20
+ === Individual Keys Provider Usage
21
+
22
+ class SecureModel < ActiveRecord::Base
23
+ acts_as_secure
24
+ end
25
+
26
+ SecureModel.with_crypto_provider(SomeProvider.new(some_param)) { SecureModel.find(:first) }
27
+ SecureModel.with_crypto_provider(SomeProvider.new(some_param)) { SecureModel.create() }
28
+
29
+ === Other Options
30
+
31
+ acts_as_secure :storage_type => :text -- changes the secure storage type from :binary to the supplied one
32
+ acts_as_secure :except => [:field1, :field2] -- disables the secure behavior for the :binary type fields in a supplied list
33
+
34
+ == Example
35
+
36
+ Let's define three models: Fruit (not secure), SecretFruit (uses the master key approach), and UberSecretFruit (uses the individual key approach).
37
+
38
+ Rot13CryptoProvider represents a master key provider. SaltedRot13CryptoProvider depends on a salt individual for each record.
39
+
40
+
41
+ === Models
42
+
43
+ class Fruit < ActiveRecord::Base
44
+ has_one :secret_fruit
45
+ has_one :uber_secret_fruit
46
+ end
47
+
48
+ class CreateFruits < ActiveRecord::Migration
49
+ def self.up
50
+ create_table :fruits do |t|
51
+ t.column :name, :string
52
+ end
53
+ end
54
+ end
55
+
56
+ class Rot13CryptoProvider
57
+ class << self
58
+ def encrypt(arg)
59
+ arg.tr("A-Za-z", "N-ZA-Mn-za-m")
60
+ end
61
+ alias_method :decrypt, :encrypt
62
+ end
63
+ end
64
+
65
+ class SecretFruit < ActiveRecord::Base
66
+ acts_as_secure :crypto_provider => Rot13CryptoProvider
67
+ belongs_to :fruit
68
+ end
69
+
70
+ class CreateSecretFruits < ActiveRecord::Migration
71
+ def self.up
72
+ create_table :secret_fruits do |t|
73
+ t.column :name, :binary
74
+ t.column :fruit_id, :integer
75
+ end
76
+ end
77
+ end
78
+
79
+ class SaltedRot13CryptoProvider
80
+ def initialize(salt)
81
+ @salt = salt
82
+ end
83
+ def encrypt(arg)
84
+ @salt + arg.tr("A-Za-z", "N-ZA-Mn-za-m")
85
+ end
86
+ def decrypt(arg)
87
+ arg[@salt.size .. -1].tr("A-Za-z", "N-ZA-Mn-za-m")
88
+ end
89
+ end
90
+
91
+ class UberSecretFruit < ActiveRecord::Base
92
+ acts_as_secure
93
+ belongs_to :fruit
94
+ end
95
+
96
+ class CreateUberSecretFruits < ActiveRecord::Migration
97
+ def self.up
98
+ create_table :uber_secret_fruits do |t|
99
+ t.column :name, :binary
100
+ t.column :fruit_id, :integer
101
+ end
102
+ end
103
+ end
104
+
105
+ === Usage
106
+
107
+ >> f = Fruit.create(:name => 'passion fruit')
108
+ >> SecretFruit.create(:name => 'maracuya', :fruit => f)
109
+ >> puts f.secret_fruit.name
110
+ maracuya
111
+ >> secret = readline.chomp
112
+ uber_secret
113
+ >> crypto_provider = SaltedRot13CryptoProvider.new(secret)
114
+ >> UberSecretFruit.with_crypto_provider(crypto_provider) { UberSecretFruit.create(:name => 'Passiflora edulis', :fruit => f) }
115
+ >> UberSecretFruit.with_crypto_provider(crypto_provider) { puts f.uber_secret_fruit.name }
116
+ Passiflora edulis
117
+
118
+ === DB
119
+
120
+ > select * from secret_fruits;
121
+ +----+---------------+----------+
122
+ | id | name | fruit_id |
123
+ +----+---------------+----------+
124
+ | 1 | --- znenphln | 1 |
125
+ +----+---------------+----------+
126
+
127
+ > select * from uber_secret_fruits;
128
+ +----+-----------------------------------+----------+
129
+ | id | name | fruit_id |
130
+ +----+-----------------------------------+----------+
131
+ | 1 | uber_secret--- Cnffvsyben rqhyvf | 1 |
132
+ +----+-----------------------------------+----------+
133
+
134
+
135
+ == Installation
136
+
137
+ As plugin:
138
+ script/plugin install svn://rubyforge.org/var/svn/acts-as-secure/trunk/vendor/plugins/acts_as_secure
139
+
140
+
141
+ == License
142
+
143
+ ActsAsSecure released under the MIT license.
144
+
145
+
146
+ == Support
147
+
148
+ The plugin RubyForge page is http://rubyforge.org/projects/acts-as-secure
149
+
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rubygems'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+
6
+ desc 'Default Task'
7
+ task :default => [:package]
8
+
9
+ gem_spec = Gem::Specification.new do |s|
10
+ s.platform = Gem::Platform::RUBY
11
+ s.name = "acts_as_secure"
12
+ s.version = "0.0.4"
13
+ s.author = "RHG Team"
14
+ s.email = "rails-trunk@revolution.com"
15
+ s.summary = "Adds attribute encryption to ActiveRecord models"
16
+ s.files = FileList["init.rb", "lib/**/*"].to_a
17
+ s.require_path = "lib"
18
+ s.has_rdoc = false
19
+ s.extra_rdoc_files = ['README', 'MIT-LICENSE']
20
+ s.autorequire = 'acts_as_secure'
21
+ end
22
+
23
+ gem = Rake::GemPackageTask.new(gem_spec) do |pkg|
24
+ pkg.need_tar = true
25
+ pkg.need_zip = true
26
+ end
27
+
28
+ desc 'Generate documentation'
29
+ Rake::RDocTask.new(:rdoc) do |rdoc|
30
+ rdoc.rdoc_dir = 'rdoc'
31
+ rdoc.title = 'ActsAsSecure'
32
+ rdoc.options << '--line-numbers' << '--inline-source'
33
+ rdoc.rdoc_files.include('README')
34
+ rdoc.rdoc_files.include('lib/**/*.rb')
35
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_secure'
@@ -0,0 +1,133 @@
1
+ # Copyright (c) 2007 Revolution Health Group LLC. All rights reserved.
2
+
3
+ module ActiveRecord; module Acts; end; end
4
+
5
+ module ActiveRecord::Acts::ActsAsSecure
6
+
7
+ require 'yaml'
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def acts_as_secure(options = {})
16
+ parse_options!(options)
17
+ add_callbacks
18
+ extend(ActsAsSecureClassMethods)
19
+ send(:include, InstanceMethods)
20
+ end
21
+
22
+ private
23
+
24
+ def parse_options!(options)
25
+ @secure_except = filter_secure_columns(options.delete(:except))
26
+ @secure_only = filter_secure_columns(options.delete(:only))
27
+ @secure_storage_type = options.delete(:storage_type) || :text
28
+ @secure_crypto_provider = options.delete(:crypto_provider)
29
+ fail("Unknown option(s): #{ options.keys.join(', ') }") unless options.empty?
30
+ end
31
+
32
+ def add_callbacks
33
+ before_save :encrypt_secure_columns
34
+ after_save :decrypt_secure_columns
35
+ after_find :decrypt_secure_columns
36
+ define_method(:after_find) { }
37
+ secure_column_symbols.each {|col| serialize col}
38
+ end
39
+
40
+ def filter_secure_columns(*names)
41
+ names.flatten.collect(&:to_s)
42
+ end
43
+
44
+ def secure_column_symbols
45
+ cols = columns.reject { |col| !@secure_only.include?(col.name) }
46
+ cols.reject { |col| (col.type != @secure_storage_type) || @secure_except.include?(col.name) }
47
+ c_sym = []
48
+ cols.each { |col| c_sym.push(col.name.to_sym) }
49
+ c_sym
50
+ end
51
+
52
+ module ActsAsSecureClassMethods
53
+
54
+ def inherited(sub)
55
+
56
+ [:secure_except, :secure_storage_type, :secure_crypto_provider].each do |p|
57
+ sub.instance_variable_set("@#{ p }", instance_variable_get("@#{ p }"))
58
+ end
59
+
60
+ super
61
+
62
+ end
63
+
64
+ def with_crypto_provider(provider)
65
+ begin
66
+ original_provider = @secure_crypto_provider
67
+ @secure_crypto_provider = provider
68
+ yield
69
+ ensure
70
+ @secure_crypto_provider = original_provider
71
+ end
72
+ end
73
+
74
+ def secure_columns
75
+ cols = columns.reject { |col| !@secure_only.include?(col.name) }
76
+ cols.reject { |col| (col.type != @secure_storage_type) || @secure_except.include?(col.name) }
77
+ end
78
+
79
+ def secure_crypto_provider
80
+ @secure_crypto_provider
81
+ end
82
+
83
+ def update_pk_password(pk_password)
84
+ @secure_crypto_provider.pk_password = pk_password
85
+ end
86
+ end
87
+
88
+
89
+ module InstanceMethods
90
+
91
+ def encrypt_secure_columns
92
+ self.class.secure_columns.each do |col|
93
+ unless self[col.name].nil?
94
+ enc = secure_encrypt(self[col.name])
95
+ self[col.name] = enc unless enc.nil?
96
+ end
97
+ end
98
+ end
99
+
100
+ def decrypt_secure_columns
101
+ self.class.secure_columns.each do |col|
102
+ unless self[col.name].nil?
103
+ dec = secure_decrypt(self[col.name])
104
+ self[col.name] = dec unless dec.nil?
105
+ end
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def secure_encrypt(arg)
112
+ secure_crypto_provider.encrypt(arg)
113
+ end
114
+
115
+ def secure_decrypt(arg)
116
+ begin
117
+ secure_crypto_provider.decrypt(arg)
118
+ rescue Exception => ex
119
+ raise "Failed to decode the field. Incorrect key?: #{ex.message}"
120
+ end
121
+ end
122
+
123
+ def secure_crypto_provider
124
+ self.class.secure_crypto_provider || fail('No crypto provider defined')
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ end
132
+
133
+ ActiveRecord::Base.send(:include, ActiveRecord::Acts::ActsAsSecure)
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quantipay-acts_as_secure
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Revolution on Rails
8
+ - Joe Scharf
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-02-10 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Acts as secure adds automatic encryption and decryption to an ActiveRecord model
18
+ email: joe@obility.net
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - CHANGELOG
27
+ - init.rb
28
+ - lib/acts_as_secure.rb
29
+ - MIT-LICENSE
30
+ - Rakefile
31
+ - README
32
+ has_rdoc: true
33
+ homepage: http://github.com/quantipay/acts_as_secure
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --inline-source
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: acts_as_secured
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Acts as secure adds automatic encryption and decryption to an ActiveRecord model
61
+ test_files: []
62
+