Permalink
Browse files

Added WHERE support

  • Loading branch information...
1 parent ccccfc0 commit 45a7471dd05bacc0f04a12e7e69ed7caf7e23a86 Flip Sasser committed Mar 28, 2012
Showing with 86 additions and 7 deletions.
  1. +2 −1 .gitignore
  2. +2 −0 .rspec
  3. +5 −0 CHANGES
  4. +16 −1 Gemfile
  5. +2 −1 lib/generators/pgcrypto/install/templates/migration.rb
  6. +43 −0 lib/pgcrypto/active_record.rb
  7. +6 −0 lib/pgcrypto/column.rb
  8. +2 −2 lib/pgcrypto/key.rb
  9. +8 −2 pgcrypto.gemspec
View
3 .gitignore
@@ -1,3 +1,4 @@
-coverage/
*.gem
.DS_Store
+coverage/
+Gemfile.lock
View
2 .rspec
@@ -1 +1,3 @@
+--backtrace
--colour
+--format Fuubar
View
5 CHANGES
@@ -1,3 +1,8 @@
+0.1.1
+ * Rebuilt the WHERE clause stuff to make sure finders AND setters
+ both worked. It's fragile and hackish at this time, but we'll get
+ there, folks!
+
0.1.0
* Overhauled the underpinnings to rely on a separate column. Adds
on-demand loading of encrypted attributes from a central table
View
17 Gemfile
@@ -1 +1,16 @@
-gem 'activerecord', '>= 3.2'
+source 'http://rubygems.org'
+
+gem 'activerecord', '>= 3.2', :require => 'active_record'
+
+group :test do
+ gem 'autotest'
+ gem 'database_cleaner', '>= 0.7'
+ gem 'fuubar'
+ gem 'pg', '>= 0.11'
+ gem 'rspec', rspec_version = '>= 2.6'
+ gem 'simplecov', :require => false
+ if RUBY_PLATFORM =~ /darwin/
+ gem 'autotest-fsevent', '>= 0.2'
+ gem 'autotest-growl', '>= 0.2'
+ end
+end
View
3 lib/generators/pgcrypto/install/templates/migration.rb
@@ -2,7 +2,8 @@ class InstallPgcrypto < ActiveRecord::Migration
def up
create_table :pgcrypto_columns do |t|
t.belongs_to :owner, :polymorphic => true
- t.string :name, :limit => 64
+ t.string :owner_table, :limit => 32
+ t.string :name, :limit => 32
t.binary :value
end
add_index :pgcrypto_columns, [:owner_id, :owner_type, :name], :name => :pgcrypto_column_finder
View
43 lib/pgcrypto/active_record.rb
@@ -9,6 +9,8 @@ def to_sql(arel, *args)
case arel
when Arel::InsertManager
pgcrypto_tweak_insert(arel, *args)
+ when Arel::SelectManager
+ pgcrypto_tweak_select(arel)
when Arel::UpdateManager
pgcrypto_tweak_update(arel)
end
@@ -35,6 +37,47 @@ def pgcrypto_tweak_insert(arel, *args)
end
end
+ def pgcrypto_tweak_select(arel)
+ # We start by looping through each "core," which is just
+ # a SelectStatement and correcting plain-text queries
+ # against an encrypted column...
+ joins = {}
+ table_name = nil
+ arel.ast.cores.each do |core|
+ # Yeah, I'm lazy. Whatevs.
+ next unless core.is_a?(Arel::Nodes::SelectCore) && !(columns = PGCrypto[table_name = core.source.left.name]).empty?
+
+ # We loop through each WHERE specification to determine whether or not the
+ # PGCrypto column should be JOIN'd upon; in which case, we, like, do it.
+ core.wheres.each do |where|
+ # Now loop through the children to encrypt them for the SELECT
+ where.children.each do |child|
+ if options = columns[child.left.name]
+ if key = PGCrypto.keys[options[:private_key] || :private]
+ join_name = "pgcrypto_column_#{child.left.name}"
+ joins[join_name] ||= {:column => child.left.name, :key => "#{key.dearmored} AS #{key.name}_key"}
+ child.left = Arel::Nodes::SqlLiteral.new(%[pgp_pub_decrypt("#{join_name}"."value", keys.#{key.name}_key)])
+ end
+ end
+ end if where.respond_to?(:children)
+ end
+ end
+ unless joins.empty?
+ key_joins = []
+ joins.each do |key_name, join|
+ key_joins.push(join[:key])
+ column = quote_string(join[:column].to_s)
+ arel.join(Arel::Nodes::SqlLiteral.new(%[
+ JOIN "pgcrypto_columns" AS "pgcrypto_column_#{column}" ON
+ "pgcrypto_column_#{column}"."owner_id" = "#{table_name}"."id"
+ AND "pgcrypto_column_#{column}"."owner_table" = '#{quote_string(table_name)}'
+ AND "pgcrypto_column_#{column}"."name" = '#{column}'
+ ]))
+ end
+ arel.join(Arel::Nodes::SqlLiteral.new("CROSS JOIN (SELECT #{key_joins.join(', ')}) AS keys"))
+ end
+ end
+
def pgcrypto_tweak_update(arel)
if arel.ast.relation.name == PGCrypto::Column.table_name
# Loop through the assignments and make sure we take care of that whole
View
6 lib/pgcrypto/column.rb
@@ -1,6 +1,12 @@
module PGCrypto
class Column < ActiveRecord::Base
self.table_name = 'pgcrypto_columns'
+ before_save :set_owner_table
belongs_to :owner, :autosave => false, :inverse_of => :pgcrypto_columns, :polymorphic => true
+
+ protected
+ def set_owner_table
+ self.owner_table = self.owner.class.table_name
+ end
end
end
View
4 lib/pgcrypto/key.rb
@@ -24,7 +24,7 @@ def dearmored
def initialize(options = {})
if options.is_a?(String)
- self.value = key
+ self.value = options
elsif options.is_a?(Hash)
options.each do |key, value|
send("#{key}=", value)
@@ -34,7 +34,7 @@ def initialize(options = {})
def path=(keyfile)
keyfile = File.expand_path(keyfile)
- raise PGCrypto::Error, "\#{keyfile} does not exist!" unless File.file?(keyfile)
+ raise PGCrypto::Error, "#{keyfile} does not exist!" unless File.file?(keyfile)
@path = keyfile
self.value = File.read(keyfile)
end
View
10 pgcrypto.gemspec
@@ -5,7 +5,7 @@
Gem::Specification.new do |s|
s.name = "pgcrypto"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Flip Sasser"]
@@ -18,12 +18,14 @@ Gem::Specification.new do |s|
]
s.files = [
".rspec",
+ ".simplecov",
"CHANGES",
"Gemfile",
"LICENSE",
"README.markdown",
"Rakefile",
"VERSION",
+ "autotest/discover.rb",
"lib/generators/pgcrypto/install/USAGE",
"lib/generators/pgcrypto/install/install_generator.rb",
"lib/generators/pgcrypto/install/templates/initializer.rb",
@@ -34,7 +36,11 @@ Gem::Specification.new do |s|
"lib/pgcrypto/column.rb",
"lib/pgcrypto/key.rb",
"lib/pgcrypto/table_manager.rb",
- "pgcrypto.gemspec"
+ "pgcrypto.gemspec",
+ "spec/lib/pgcrypto_spec.rb",
+ "spec/spec_helper.rb",
+ "spec/support/private.key",
+ "spec/support/public.key"
]
s.homepage = "http://github.com/Plinq/pgcrypto"
s.require_paths = ["lib"]

0 comments on commit 45a7471

Please sign in to comment.