pgcrypto 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec CHANGED
@@ -1 +1,3 @@
1
+ --backtrace
1
2
  --colour
3
+ --format Fuubar
data/.simplecov ADDED
@@ -0,0 +1,4 @@
1
+ SimpleCov.start do
2
+ add_filter "test.rb"
3
+ add_filter "/spec/"
4
+ end
data/CHANGES CHANGED
@@ -1,3 +1,8 @@
1
+ 0.1.1
2
+ * Rebuilt the WHERE clause stuff to make sure finders AND setters
3
+ both worked. It's fragile and hackish at this time, but we'll get
4
+ there, folks!
5
+
1
6
  0.1.0
2
7
  * Overhauled the underpinnings to rely on a separate column. Adds
3
8
  on-demand loading of encrypted attributes from a central table
data/Gemfile CHANGED
@@ -1 +1,16 @@
1
- gem 'activerecord', '>= 3.2'
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'activerecord', '>= 3.2', :require => 'active_record'
4
+
5
+ group :test do
6
+ gem 'autotest'
7
+ gem 'database_cleaner', '>= 0.7'
8
+ gem 'fuubar'
9
+ gem 'pg', '>= 0.11'
10
+ gem 'rspec', rspec_version = '>= 2.6'
11
+ gem 'simplecov', :require => false
12
+ if RUBY_PLATFORM =~ /darwin/
13
+ gem 'autotest-fsevent', '>= 0.2'
14
+ gem 'autotest-growl', '>= 0.2'
15
+ end
16
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -0,0 +1,14 @@
1
+ require 'autotest/fsevent'
2
+ require 'autotest/growl'
3
+
4
+ Autotest.add_discovery { "rspec2" }
5
+
6
+ Autotest.add_hook :initialize do |autotest|
7
+ autotest.add_mapping %r(^lib/**/*\.rb$) do |file, _|
8
+ Dir['spec/**/*.rb']
9
+ end
10
+
11
+ autotest.add_mapping %r(^spec/support/*\.rb$) do |file, _|
12
+ Dir['spec/**/*.rb']
13
+ end
14
+ end
@@ -2,7 +2,8 @@ class InstallPgcrypto < ActiveRecord::Migration
2
2
  def up
3
3
  create_table :pgcrypto_columns do |t|
4
4
  t.belongs_to :owner, :polymorphic => true
5
- t.string :name, :limit => 64
5
+ t.string :owner_table, :limit => 32
6
+ t.string :name, :limit => 32
6
7
  t.binary :value
7
8
  end
8
9
  add_index :pgcrypto_columns, [:owner_id, :owner_type, :name], :name => :pgcrypto_column_finder
@@ -9,6 +9,8 @@ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
9
9
  case arel
10
10
  when Arel::InsertManager
11
11
  pgcrypto_tweak_insert(arel, *args)
12
+ when Arel::SelectManager
13
+ pgcrypto_tweak_select(arel)
12
14
  when Arel::UpdateManager
13
15
  pgcrypto_tweak_update(arel)
14
16
  end
@@ -35,6 +37,47 @@ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
35
37
  end
36
38
  end
37
39
 
40
+ def pgcrypto_tweak_select(arel)
41
+ # We start by looping through each "core," which is just
42
+ # a SelectStatement and correcting plain-text queries
43
+ # against an encrypted column...
44
+ joins = {}
45
+ table_name = nil
46
+ arel.ast.cores.each do |core|
47
+ # Yeah, I'm lazy. Whatevs.
48
+ next unless core.is_a?(Arel::Nodes::SelectCore) && !(columns = PGCrypto[table_name = core.source.left.name]).empty?
49
+
50
+ # We loop through each WHERE specification to determine whether or not the
51
+ # PGCrypto column should be JOIN'd upon; in which case, we, like, do it.
52
+ core.wheres.each do |where|
53
+ # Now loop through the children to encrypt them for the SELECT
54
+ where.children.each do |child|
55
+ if options = columns[child.left.name]
56
+ if key = PGCrypto.keys[options[:private_key] || :private]
57
+ join_name = "pgcrypto_column_#{child.left.name}"
58
+ joins[join_name] ||= {:column => child.left.name, :key => "#{key.dearmored} AS #{key.name}_key"}
59
+ child.left = Arel::Nodes::SqlLiteral.new(%[pgp_pub_decrypt("#{join_name}"."value", keys.#{key.name}_key)])
60
+ end
61
+ end
62
+ end if where.respond_to?(:children)
63
+ end
64
+ end
65
+ unless joins.empty?
66
+ key_joins = []
67
+ joins.each do |key_name, join|
68
+ key_joins.push(join[:key])
69
+ column = quote_string(join[:column].to_s)
70
+ arel.join(Arel::Nodes::SqlLiteral.new(%[
71
+ JOIN "pgcrypto_columns" AS "pgcrypto_column_#{column}" ON
72
+ "pgcrypto_column_#{column}"."owner_id" = "#{table_name}"."id"
73
+ AND "pgcrypto_column_#{column}"."owner_table" = '#{quote_string(table_name)}'
74
+ AND "pgcrypto_column_#{column}"."name" = '#{column}'
75
+ ]))
76
+ end
77
+ arel.join(Arel::Nodes::SqlLiteral.new("CROSS JOIN (SELECT #{key_joins.join(', ')}) AS keys"))
78
+ end
79
+ end
80
+
38
81
  def pgcrypto_tweak_update(arel)
39
82
  if arel.ast.relation.name == PGCrypto::Column.table_name
40
83
  # Loop through the assignments and make sure we take care of that whole
@@ -1,6 +1,12 @@
1
1
  module PGCrypto
2
2
  class Column < ActiveRecord::Base
3
3
  self.table_name = 'pgcrypto_columns'
4
+ before_save :set_owner_table
4
5
  belongs_to :owner, :autosave => false, :inverse_of => :pgcrypto_columns, :polymorphic => true
6
+
7
+ protected
8
+ def set_owner_table
9
+ self.owner_table = self.owner.class.table_name
10
+ end
5
11
  end
6
12
  end
data/lib/pgcrypto/key.rb CHANGED
@@ -24,7 +24,7 @@ module PGCrypto
24
24
 
25
25
  def initialize(options = {})
26
26
  if options.is_a?(String)
27
- self.value = key
27
+ self.value = options
28
28
  elsif options.is_a?(Hash)
29
29
  options.each do |key, value|
30
30
  send("#{key}=", value)
@@ -34,7 +34,7 @@ module PGCrypto
34
34
 
35
35
  def path=(keyfile)
36
36
  keyfile = File.expand_path(keyfile)
37
- raise PGCrypto::Error, "\#{keyfile} does not exist!" unless File.file?(keyfile)
37
+ raise PGCrypto::Error, "#{keyfile} does not exist!" unless File.file?(keyfile)
38
38
  @path = keyfile
39
39
  self.value = File.read(keyfile)
40
40
  end
data/pgcrypto.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "pgcrypto"
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Flip Sasser"]
@@ -18,12 +18,14 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.files = [
20
20
  ".rspec",
21
+ ".simplecov",
21
22
  "CHANGES",
22
23
  "Gemfile",
23
24
  "LICENSE",
24
25
  "README.markdown",
25
26
  "Rakefile",
26
27
  "VERSION",
28
+ "autotest/discover.rb",
27
29
  "lib/generators/pgcrypto/install/USAGE",
28
30
  "lib/generators/pgcrypto/install/install_generator.rb",
29
31
  "lib/generators/pgcrypto/install/templates/initializer.rb",
@@ -34,7 +36,11 @@ Gem::Specification.new do |s|
34
36
  "lib/pgcrypto/column.rb",
35
37
  "lib/pgcrypto/key.rb",
36
38
  "lib/pgcrypto/table_manager.rb",
37
- "pgcrypto.gemspec"
39
+ "pgcrypto.gemspec",
40
+ "spec/lib/pgcrypto_spec.rb",
41
+ "spec/spec_helper.rb",
42
+ "spec/support/private.key",
43
+ "spec/support/public.key"
38
44
  ]
39
45
  s.homepage = "http://github.com/Plinq/pgcrypto"
40
46
  s.require_paths = ["lib"]
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe PGCrypto do
4
+ it "should extend ActiveRecord::Base" do
5
+ PGCryptoTestModel.should respond_to(:pgcrypto)
6
+ end
7
+
8
+ describe "attributes" do
9
+ before :each do
10
+ PGCryptoTestModel.pgcrypto :test_column
11
+ end
12
+
13
+ it "should have readers and writers" do
14
+ model = PGCryptoTestModel.new
15
+ model.should respond_to(:test_column)
16
+ model.should respond_to(:test_column=)
17
+ end
18
+
19
+ it "be settable on create" do
20
+ model = PGCryptoTestModel.new(:test_column => 'this is a test')
21
+ model.save!.should be_true
22
+ end
23
+
24
+ it "be settable on update" do
25
+ model = PGCryptoTestModel.create!
26
+ model.test_column = 'this is another test'
27
+ model.save!.should be_true
28
+ end
29
+
30
+ it "be update-able" do
31
+ model = PGCryptoTestModel.create!(:test_column => 'i am test column')
32
+ model.update_attributes!(:test_column => 'but now i am a different column, son').should be_true
33
+ end
34
+
35
+ it "be retrievable at create" do
36
+ model = PGCryptoTestModel.create!(:test_column => 'i am test column')
37
+ model.test_column.should == 'i am test column'
38
+ end
39
+
40
+ it "be retrievable after create" do
41
+ model = PGCryptoTestModel.create!(:test_column => 'i should return to you')
42
+ PGCryptoTestModel.find(model.id).test_column.should == 'i should return to you'
43
+ end
44
+
45
+ it "be searchable" do
46
+ model = PGCryptoTestModel.create!(:test_column => 'i am findable!')
47
+ PGCryptoTestModel.where(:test_column => model.test_column).count.should == 1
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,79 @@
1
+ # Add lib/ to the load path
2
+ $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__)))
3
+
4
+ # Load up our Gemfile
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ Bundler.require(:default, :test)
8
+
9
+ # Enable coverage reporting
10
+ require 'simplecov'
11
+
12
+ # Requier and configure PGCrypto
13
+ require 'pgcrypto'
14
+ PGCrypto.keys[:private] = {:path => File.join(File.dirname(__FILE__), 'support', 'private.key')}
15
+ PGCrypto.keys[:public] = {:path => File.join(File.dirname(__FILE__), 'support', 'public.key')}
16
+
17
+ RSpec.configure do |config|
18
+ database_config = {:adapter => 'postgresql', :database => 'pgcrypto_test', :encoding => 'utf8', :host => 'localhost'}
19
+ postgres_config = database_config.merge(:database => 'postgres', :schema_search_path => 'public')
20
+
21
+ # Set up the database to handle pgcrypto functions and the schema for
22
+ # our tests
23
+ config.before :all do
24
+ # Connect to the local postgres schema database
25
+ ActiveRecord::Base.establish_connection(postgres_config)
26
+
27
+ # Create the test database if we can
28
+ ActiveRecord::Base.connection.create_database(database_config[:database]) rescue nil
29
+
30
+ # Now connect to the newly created database
31
+ ActiveRecord::Base.establish_connection(database_config)
32
+
33
+
34
+ silence_stream(STDOUT) do
35
+ # ...and load in the pgcrypto extension
36
+ ActiveRecord::Base.connection.execute(%[CREATE EXTENSION pgcrypto])
37
+
38
+ # ...and then set up the pgcrypto_columns and pgcrypto_test_models fun
39
+ ActiveRecord::Schema.define do
40
+ create_table :pgcrypto_columns, :force => true do |t|
41
+ t.belongs_to :owner, :polymorphic => true
42
+ t.string :owner_table, :limit => 32
43
+ t.string :name, :limit => 64
44
+ t.binary :value
45
+ end
46
+
47
+ create_table :pgcrypto_test_models, :force => true do |t|
48
+ t.string :name, :limit => 32
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ config.before :each do
55
+ if example.metadata[:transaction] == false
56
+ DatabaseCleaner.strategy = :truncation
57
+ DatabaseCleaner.clean_with :truncation
58
+ else
59
+ DatabaseCleaner.strategy = :transaction
60
+ end
61
+ DatabaseCleaner.start
62
+
63
+ ActiveRecord::Base.establish_connection(database_config)
64
+
65
+ class PGCryptoTestModel < ActiveRecord::Base
66
+ self.table_name = :pgcrypto_test_models
67
+ end
68
+ end
69
+
70
+ config.after :all do
71
+ # Drop the database when we exist
72
+ ActiveRecord::Base.establish_connection(postgres_config)
73
+ ActiveRecord::Base.connection.drop_database(database_config[:database]) rescue nil
74
+ end
75
+
76
+ config.after :each do
77
+ DatabaseCleaner.clean
78
+ end
79
+ end
@@ -0,0 +1,34 @@
1
+ -----BEGIN PGP PRIVATE KEY BLOCK-----
2
+ Version: GnuPG v1.4.12 (Darwin)
3
+
4
+ lQHYBE9ydVABBADfTmLU+G19Vz8iugTZfuxYG2KWI5+UEvW0APE0XF9R9bRca84m
5
+ +TbMmhs0hHWvH+5xmPgBN8atx1PAJj65LZufLQipXb4ZLYOi2OWPh7umlMaoSrwY
6
+ UI7SHxL/zqRkdwHiricxTlfqv4lPc7kxDjwfked8nDWcVkCreZVLTwwUfwARAQAB
7
+ AAP6Ay+UG2O79CjVfsJWpV+5MXyaiHfTpAItPTcyOcQDnCC8RQFIvKebj4m3T6WA
8
+ JFJ5TWeYSjQognwwhrJE/NFYwgLH698fEw3TEHyvXm6hdb3Aqv7wCc52pZx9E3OL
9
+ AvSQArTypTO4WsGsYIBVbpvsQervA03gxEo025q7QGPQYoECAOcC9eTlKznjDj09
10
+ 5AOueWE1s3NZDlESGkq6qEM+xIHurEvgX7jHLe0bRQWfo/Z/D5uplTrFkPTgtA/a
11
+ lwKej78CAPd2DYocgmCQ44e1Dc8Z8XSeKuMmmZqbFszDr8+4LM40irmFeiSb1S6p
12
+ rj1h2WKFhnar568tMSvxrZmu5dZ7q0EB/1kVXLAJK6PS7IhOnFMbMMQcWmQLfQ7I
13
+ pgEuUF6DG9drSbj8r5oU+PUejGmurl5ZqVWbCGHnlbTU/QsRhp8VvdegMLQ0Rmxp
14
+ cCBTYXNzZXIgKERlbGlnaHRmdWwgV2lkZ2V0cykgPGZsaXBAZ2V0cGxpbnEuY29t
15
+ Poi4BBMBAgAiBQJPcnVQAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBI
16
+ 6kAcpRMrn741BACT09j5TEG7YnjFf9eoXfh5JpYWv+xdnAJZjMdl+cMZ5CxZB+vx
17
+ zNhOX2OwCWu5fEh2wKbbRuLWHna6SebRjFzY62Ld0ijqVyRPzvz/v7Vn8pBqsTvJ
18
+ PgXUmrMmK9OAMAAM64NwRPiAxSkQtc3qG2uacDyCEI10uK9MT/zNmC6ISJ0B2ARP
19
+ cnVQAQQA9MEq2zMJtfWLZ2HfkTYkHemesc4XB+xCLfldNdmUwQ4o+GXfxe6VXYCc
20
+ KPpyx+qXSP6q7OxR8QKk8wbIord2/w6iIkn+ULvHQw1KMaCQW3JuQPB+IMWA3kY8
21
+ eQ3uibc2vk3ZFMtlU9HCxcRBPnMsCaBXYH1EkQ0+JVyuAotFOksAEQEAAQAD+gJY
22
+ HhfdLGDv4G08KXIdTlZANRMBGGss6GtcgcSKXqh3540nG9Z4+2xBI/3Br4fzp43j
23
+ ub5gLkUixqXfPdyB6Yokc+U0AQVuKZPXbFAROigD2gzpnDWaLeByL4Ua6FV6zNsl
24
+ gTLIS++r81KJBkiw0yhgqfNt44Gbta7y6nPXdMGRAgD1j+wyfvrhoAGgNkZR1h6U
25
+ A+enlaK5+TdeDFljJKo8w3io/s9abOLnVBE4rRsdZtJmK5q0rjeMNKzOsohk9EHR
26
+ AgD/KHTbvTsvrH6kcKpdCHGjyEnYAndxVbjoclHR7NRz6QlhqdNKGI0FbwmPcf7p
27
+ WOSXJX6j8u4dHrx1wZTcDMVbAf9/VsMOkzYnSQEzDV2ixunvkYmfNuD1NbYERgyG
28
+ 4Zd+TWKLPx98y/+VW8js5FkH46XgoOsYkEGjl3ZJb5kPth1rog2InwQYAQIACQUC
29
+ T3J1UAIbDAAKCRBI6kAcpRMrn/w6BADN0x89vP0LGRpnwK2Z8AVIXilcPNxe8ckq
30
+ xx0XIU5X13d6DtvNClhIu2N2fA0M2/PR7/MG8+zGoZLZUvC0D+WUNq3Y5hQb2K7J
31
+ IAVPNYVH+R05/1tWGsH/8h/eH9PvldQ8tjQV4jgLJqx5/PNrSb6WdSUmoKQrgORB
32
+ QNXpHhBr6g==
33
+ =sN2M
34
+ -----END PGP PRIVATE KEY BLOCK-----
@@ -0,0 +1,20 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: GnuPG v1.4.12 (Darwin)
3
+
4
+ mI0ET3J1UAEEAN9OYtT4bX1XPyK6BNl+7FgbYpYjn5QS9bQA8TRcX1H1tFxrzib5
5
+ NsyaGzSEda8f7nGY+AE3xq3HU8AmPrktm58tCKldvhktg6LY5Y+Hu6aUxqhKvBhQ
6
+ jtIfEv/OpGR3AeKuJzFOV+q/iU9zuTEOPB+R53ycNZxWQKt5lUtPDBR/ABEBAAG0
7
+ NEZsaXAgU2Fzc2VyIChEZWxpZ2h0ZnVsIFdpZGdldHMpIDxmbGlwQGdldHBsaW5x
8
+ LmNvbT6IuAQTAQIAIgUCT3J1UAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA
9
+ CgkQSOpAHKUTK5++NQQAk9PY+UxBu2J4xX/XqF34eSaWFr/sXZwCWYzHZfnDGeQs
10
+ WQfr8czYTl9jsAlruXxIdsCm20bi1h52uknm0Yxc2Oti3dIo6lckT878/7+1Z/KQ
11
+ arE7yT4F1JqzJivTgDAADOuDcET4gMUpELXN6htrmnA8ghCNdLivTE/8zZguiEi4
12
+ jQRPcnVQAQQA9MEq2zMJtfWLZ2HfkTYkHemesc4XB+xCLfldNdmUwQ4o+GXfxe6V
13
+ XYCcKPpyx+qXSP6q7OxR8QKk8wbIord2/w6iIkn+ULvHQw1KMaCQW3JuQPB+IMWA
14
+ 3kY8eQ3uibc2vk3ZFMtlU9HCxcRBPnMsCaBXYH1EkQ0+JVyuAotFOksAEQEAAYif
15
+ BBgBAgAJBQJPcnVQAhsMAAoJEEjqQBylEyuf/DoEAM3THz28/QsZGmfArZnwBUhe
16
+ KVw83F7xySrHHRchTlfXd3oO280KWEi7Y3Z8DQzb89Hv8wbz7MahktlS8LQP5ZQ2
17
+ rdjmFBvYrskgBU81hUf5HTn/W1Yawf/yH94f0++V1Dy2NBXiOAsmrHn882tJvpZ1
18
+ JSagpCuA5EFA1ekeEGvq
19
+ =XKvD
20
+ -----END PGP PUBLIC KEY BLOCK-----
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgcrypto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-03-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70130059262260 !ruby/object:Gem::Requirement
16
+ requirement: &70275631246800 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '3.2'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70130059262260
24
+ version_requirements: *70275631246800
25
25
  description: ! "\n PGCrypto is an ActiveRecord::Base extension that allows you
26
26
  to asymmetrically\n encrypt PostgreSQL columns with as little trouble as possible.
27
27
  It's totally\n freaking rad.\n "
@@ -33,12 +33,14 @@ extra_rdoc_files:
33
33
  - README.markdown
34
34
  files:
35
35
  - .rspec
36
+ - .simplecov
36
37
  - CHANGES
37
38
  - Gemfile
38
39
  - LICENSE
39
40
  - README.markdown
40
41
  - Rakefile
41
42
  - VERSION
43
+ - autotest/discover.rb
42
44
  - lib/generators/pgcrypto/install/USAGE
43
45
  - lib/generators/pgcrypto/install/install_generator.rb
44
46
  - lib/generators/pgcrypto/install/templates/initializer.rb
@@ -50,6 +52,10 @@ files:
50
52
  - lib/pgcrypto/key.rb
51
53
  - lib/pgcrypto/table_manager.rb
52
54
  - pgcrypto.gemspec
55
+ - spec/lib/pgcrypto_spec.rb
56
+ - spec/spec_helper.rb
57
+ - spec/support/private.key
58
+ - spec/support/public.key
53
59
  homepage: http://github.com/Plinq/pgcrypto
54
60
  licenses: []
55
61
  post_install_message: