acts_as_secure 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +149 -0
  3. data/init.rb +1 -0
  4. data/lib/acts_as_secure.rb +98 -0
  5. metadata +50 -0
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/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_secure'
@@ -0,0 +1,98 @@
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
+ @@secure_crypto_providers = {}
16
+
17
+ def acts_as_secure(options = {})
18
+ parse_options!(options)
19
+ add_callbacks
20
+ send(:include, InstanceMethods)
21
+ end
22
+
23
+ def with_crypto_provider(provider)
24
+ begin
25
+ @@secure_crypto_providers[Thread.current.object_id] = provider
26
+ yield
27
+ ensure
28
+ @@secure_crypto_providers.delete(Thread.current.object_id)
29
+ end
30
+ end
31
+
32
+ def secure_columns
33
+ columns.reject { |col| (col.type != @secure_storage_type) || @secure_except.include?(col.name) }
34
+ end
35
+
36
+ def secure_crypto_provider
37
+ @@secure_crypto_providers[Thread.current.object_id] || @secure_class_crypto_provider
38
+ end
39
+
40
+ private
41
+
42
+ def parse_options!(options)
43
+ @secure_except = unsecure_columns(options.delete(:except))
44
+ @secure_storage_type = options.delete(:storage_type) || :binary
45
+ @secure_class_crypto_provider = options.delete(:crypto_provider)
46
+ fail("Unknown option(s): #{ options.keys.join(', ') }") unless options.empty?
47
+ end
48
+
49
+ def add_callbacks
50
+ before_save :encrypt_secure_columns
51
+ after_save :decrypt_secure_columns
52
+ after_find :decrypt_secure_columns
53
+ define_method(:after_find) { }
54
+ end
55
+
56
+ def unsecure_columns(*names)
57
+ names.flatten.collect(&:to_s)
58
+ end
59
+
60
+ module InstanceMethods
61
+
62
+ def encrypt_secure_columns
63
+ self.class.secure_columns.each do |col|
64
+ self[col.name] = secure_encrypt(self[col.name])
65
+ end
66
+ end
67
+
68
+ def decrypt_secure_columns
69
+ self.class.secure_columns.each do |col|
70
+ self[col.name] = secure_decrypt(send("#{ col.name }_before_type_cast"))
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def secure_encrypt(arg)
77
+ secure_crypto_provider.encrypt(arg.to_yaml)
78
+ end
79
+
80
+ def secure_decrypt(arg)
81
+ begin
82
+ YAML.load(secure_crypto_provider.decrypt(arg))
83
+ rescue Exception => ex
84
+ raise "Failed to decode the field. Incorrect key?"
85
+ end
86
+ end
87
+
88
+ def secure_crypto_provider
89
+ self.class.secure_crypto_provider || fail('No crypto provider defined')
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ ActiveRecord::Base.send(:include, ActiveRecord::Acts::ActsAsSecure)
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: acts_as_secure
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2007-06-07 00:00:00 -04:00
8
+ summary: acts_as_secure adds attributes encryption to ActiveRecord models
9
+ require_paths:
10
+ - lib
11
+ email: rails-trunk@revolution.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: acts_as_secure
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - RHG Team
31
+ files:
32
+ - init.rb
33
+ - lib/acts_as_secure.rb
34
+ - README
35
+ - MIT-LICENSE
36
+ test_files: []
37
+
38
+ rdoc_options: []
39
+
40
+ extra_rdoc_files:
41
+ - README
42
+ - MIT-LICENSE
43
+ executables: []
44
+
45
+ extensions: []
46
+
47
+ requirements: []
48
+
49
+ dependencies: []
50
+