pgcrypto 0.3.6 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +5 -1
  3. data/Gemfile +1 -4
  4. data/README.markdown +126 -33
  5. data/VERSION +1 -1
  6. data/lib/active_record/connection_adapters/pgcrypto_adapter.rb +4 -0
  7. data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb +20 -0
  8. data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb +22 -0
  9. data/lib/pgcrypto.rb +15 -105
  10. data/lib/pgcrypto/adapter.rb +162 -0
  11. data/lib/pgcrypto/column.rb +9 -8
  12. data/lib/pgcrypto/column_converter.rb +26 -0
  13. data/lib/pgcrypto/generators/base_generator.rb +12 -0
  14. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/USAGE +0 -0
  15. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/install_generator.rb +2 -5
  16. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/templates/initializer.rb +0 -0
  17. data/lib/pgcrypto/generators/install/templates/migration.rb +5 -0
  18. data/lib/pgcrypto/generators/upgrade/USAGE +8 -0
  19. data/lib/pgcrypto/generators/upgrade/templates/migration.rb +22 -0
  20. data/lib/pgcrypto/generators/upgrade/upgrade_generator.rb +15 -0
  21. data/lib/pgcrypto/has_encrypted_column.rb +25 -0
  22. data/lib/pgcrypto/key.rb +0 -10
  23. data/lib/pgcrypto/key_manager.rb +11 -0
  24. data/lib/pgcrypto/railtie.rb +15 -0
  25. data/lib/pgcrypto/table.rb +11 -0
  26. data/lib/pgcrypto/table_manager.rb +2 -10
  27. data/lib/tasks/pgcrypto.rake +7 -0
  28. data/pgcrypto.gemspec +24 -17
  29. data/spec/lib/pgcrypto_spec.rb +101 -100
  30. data/spec/spec_helper.rb +27 -28
  31. metadata +20 -36
  32. data/lib/generators/pgcrypto/install/templates/migration.rb +0 -17
  33. data/lib/pgcrypto/active_record.rb +0 -88
  34. data/lib/pgcrypto/arel.rb +0 -24
@@ -1,24 +1,28 @@
1
+ require 'rubygems'
2
+ require 'simplecov'
3
+
1
4
  # Add lib/ to the load path
2
5
  $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__)))
3
6
 
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'
7
+ require 'database_cleaner'
8
+ require 'pry'
11
9
 
12
- # Requier and configure PGCrypto
10
+ gem 'activerecord', ENV.fetch('ACTIVE_RECORD_VERSION', '>= 4.0')
11
+ require 'active_record'
13
12
  require 'pgcrypto'
14
13
 
15
14
  RSpec.configure do |config|
16
- database_config = {:adapter => 'postgresql', :database => 'pgcrypto_test', :encoding => 'utf8', :host => 'localhost'}
15
+ database_config = {
16
+ adapter: 'pgcrypto',
17
+ database: '__pgcrypto_gem_test',
18
+ encoding: 'utf8',
19
+ host: 'localhost'
20
+ }
17
21
  postgres_config = database_config.merge(:database => 'postgres', :schema_search_path => 'public')
18
22
 
19
23
  # Set up the database to handle pgcrypto functions and the schema for
20
24
  # our tests
21
- config.before :all do
25
+ config.before :suite do
22
26
  # Connect to the local postgres schema database
23
27
  ActiveRecord::Base.establish_connection(postgres_config)
24
28
 
@@ -30,44 +34,39 @@ RSpec.configure do |config|
30
34
 
31
35
  silence_stream(STDOUT) do
32
36
  # ...and load in the pgcrypto extension
33
- ActiveRecord::Base.connection.execute(%[CREATE EXTENSION pgcrypto])
37
+ ActiveRecord::Base.connection.execute(%[CREATE EXTENSION pgcrypto]) rescue nil
34
38
 
35
39
  # ...and then set up the pgcrypto_columns and pgcrypto_test_models fun
36
40
  ActiveRecord::Schema.define do
37
- create_table :pgcrypto_columns, :force => true do |t|
38
- t.belongs_to :owner, :polymorphic => true
39
- t.string :owner_table, :limit => 32
40
- t.string :name, :limit => 64
41
- t.binary :value
42
- end
43
-
44
41
  create_table :pgcrypto_test_models, :force => true do |t|
45
42
  t.string :name, :limit => 32
43
+ t.binary :encrypted_text
46
44
  end
47
45
  end
48
46
  end
47
+
48
+ ActiveRecord::Base.establish_connection(database_config)
49
+
50
+ DatabaseCleaner.strategy = :transaction
49
51
  end
50
52
 
51
53
  config.before :each do
52
- DatabaseCleaner.strategy = :transaction
53
- DatabaseCleaner.clean_with :transaction
54
54
  DatabaseCleaner.start
55
55
 
56
- ActiveRecord::Base.establish_connection(database_config)
57
-
58
56
  class PGCryptoTestModel < ActiveRecord::Base
59
57
  self.table_name = :pgcrypto_test_models
60
- pgcrypto :test_column
58
+ has_encrypted_column :encrypted_text
61
59
  end
62
60
  end
63
61
 
64
- config.after :all do
65
- # Drop the database when we exist
62
+ config.after :each do
63
+ DatabaseCleaner.clean
64
+ end
65
+
66
+ config.after :suite do
67
+ # Drop the database when we exit
66
68
  ActiveRecord::Base.establish_connection(postgres_config)
67
69
  ActiveRecord::Base.connection.drop_database(database_config[:database]) rescue nil
68
70
  end
69
71
 
70
- config.after :each do
71
- DatabaseCleaner.clean
72
- end
73
72
  end
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgcrypto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flip Sasser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-22 00:00:00.000000000 Z
11
+ date: 2014-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: activerecord
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '3.2'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '3.2'
27
- - !ruby/object:Gem::Dependency
28
- name: big_spoon
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 0.2.1
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: 0.2.1
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: jeweler
43
15
  requirement: !ruby/object:Gem::Requirement
@@ -71,16 +43,28 @@ files:
71
43
  - README.markdown
72
44
  - Rakefile
73
45
  - VERSION
74
- - lib/generators/pgcrypto/install/USAGE
75
- - lib/generators/pgcrypto/install/install_generator.rb
76
- - lib/generators/pgcrypto/install/templates/initializer.rb
77
- - lib/generators/pgcrypto/install/templates/migration.rb
46
+ - lib/active_record/connection_adapters/pgcrypto_adapter.rb
47
+ - lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb
48
+ - lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb
78
49
  - lib/pgcrypto.rb
79
- - lib/pgcrypto/active_record.rb
80
- - lib/pgcrypto/arel.rb
50
+ - lib/pgcrypto/adapter.rb
81
51
  - lib/pgcrypto/column.rb
52
+ - lib/pgcrypto/column_converter.rb
53
+ - lib/pgcrypto/generators/base_generator.rb
54
+ - lib/pgcrypto/generators/install/USAGE
55
+ - lib/pgcrypto/generators/install/install_generator.rb
56
+ - lib/pgcrypto/generators/install/templates/initializer.rb
57
+ - lib/pgcrypto/generators/install/templates/migration.rb
58
+ - lib/pgcrypto/generators/upgrade/USAGE
59
+ - lib/pgcrypto/generators/upgrade/templates/migration.rb
60
+ - lib/pgcrypto/generators/upgrade/upgrade_generator.rb
61
+ - lib/pgcrypto/has_encrypted_column.rb
82
62
  - lib/pgcrypto/key.rb
63
+ - lib/pgcrypto/key_manager.rb
64
+ - lib/pgcrypto/railtie.rb
65
+ - lib/pgcrypto/table.rb
83
66
  - lib/pgcrypto/table_manager.rb
67
+ - lib/tasks/pgcrypto.rake
84
68
  - pgcrypto.gemspec
85
69
  - spec/lib/pgcrypto_spec.rb
86
70
  - spec/spec_helper.rb
@@ -1,17 +0,0 @@
1
- class InstallPgcrypto < ActiveRecord::Migration
2
- def up
3
- create_table :pgcrypto_columns do |t|
4
- t.belongs_to :owner, :polymorphic => true
5
- t.string :owner_table, :limit => 32
6
- t.string :name, :limit => 32
7
- t.binary :value
8
- end
9
- add_index :pgcrypto_columns, [:owner_id, :owner_type, :name], :name => :pgcrypto_type_finder
10
- add_index :pgcrypto_columns, [:owner_id, :owner_table, :name], :name => :pgcrypto_table_finder
11
- execute("CREATE EXTENSION IF NOT EXISTS pgcrypto")
12
- end
13
-
14
- def down
15
- drop_table :pgcrypto_columns
16
- end
17
- end
@@ -1,88 +0,0 @@
1
- require 'active_record/connection_adapters/postgresql_adapter'
2
-
3
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
4
- unless instance_methods.include?(:to_sql_without_pgcrypto) || instance_methods.include?('to_sql_without_pgcrypto')
5
- alias :to_sql_without_pgcrypto :to_sql
6
- end
7
-
8
- def to_sql(arel, *args)
9
- case arel
10
- when Arel::InsertManager
11
- pgcrypto_tweak_insert(arel)
12
- when Arel::SelectManager
13
- pgcrypto_tweak_select(arel)
14
- when Arel::UpdateManager
15
- pgcrypto_tweak_update(arel)
16
- end
17
- to_sql_without_pgcrypto(arel, *args)
18
- end
19
-
20
- private
21
- def pgcrypto_tweak_insert(arel)
22
- if arel.ast.relation.name.to_s == PGCrypto::Column.table_name.to_s
23
- return unless key = PGCrypto.keys[:public]
24
- arel.ast.columns.each_with_index do |column, i|
25
- if column.name == 'value'
26
- value = arel.ast.values.expressions[i]
27
- quoted_value = quote_string(value)
28
- encryption_instruction = %[pgp_pub_encrypt(#{quoted_value}, #{key.dearmored})]
29
- arel.ast.values.expressions[i] = Arel::Nodes::SqlLiteral.new(encryption_instruction)
30
- end
31
- end
32
- end
33
- end
34
-
35
- def pgcrypto_tweak_select(arel)
36
- return unless key = PGCrypto.keys[:private]
37
- # We start by looping through each "core," which is just
38
- # a SelectStatement and correcting plain-text queries
39
- # against an encrypted column...
40
- joins = []
41
- table_name = nil
42
- arel.ast.cores.each do |core|
43
- # Yeah, I'm lazy. Whatevs.
44
- next unless core.is_a?(Arel::Nodes::SelectCore)
45
-
46
- encrypted_columns = PGCrypto[table_name = core.source.left.name]
47
- next if encrypted_columns.empty?
48
-
49
- # We loop through each WHERE specification to determine whether or not the
50
- # PGCrypto column should be JOIN'd upon; in which case, we, like, do it.
51
- core.wheres.each do |where|
52
- # Now loop through the children to encrypt them for the SELECT
53
- where.children.each do |child|
54
- next unless encrypted_columns[child.left.name.to_s]
55
- joins.push(child.left.name.to_s) unless joins.include?(child.left.name.to_s)
56
- child.left = Arel::Nodes::SqlLiteral.new(%[
57
- pgp_pub_decrypt("#{PGCrypto::Column.table_name}_#{child.left.name}"."value", pgcrypto_keys.#{key.name}#{key.password?})
58
- ])
59
- end if where.respond_to?(:children)
60
- end
61
- end
62
- if joins.any?
63
- arel.join(Arel::Nodes::SqlLiteral.new("CROSS JOIN (SELECT #{key.dearmored} AS #{key.name}) AS pgcrypto_keys"))
64
- joins.each do |column|
65
- column = quote_string(column)
66
- as_table = "#{PGCrypto::Column.table_name}_#{column}"
67
- arel.join(Arel::Nodes::SqlLiteral.new(%[
68
- JOIN "#{PGCrypto::Column.table_name}" AS "#{as_table}" ON "#{as_table}"."owner_id" = "#{table_name}"."id" AND "#{as_table}"."owner_table" = '#{quote_string(table_name)}' AND "#{as_table}"."name" = '#{column}'
69
- ]))
70
- end
71
- end
72
- end
73
-
74
- def pgcrypto_tweak_update(arel)
75
- if arel.ast.relation.name.to_s == PGCrypto::Column.table_name.to_s
76
- # Loop through the assignments and make sure we take care of that whole
77
- # NULL value thing!
78
- value = arel.ast.values.select{|value| value.respond_to?(:left) && value.left.name == 'value' }.first
79
- if value.right.nil?
80
- value.right = Arel::Nodes::SqlLiteral.new('NULL')
81
- elsif key = PGCrypto.keys[:public]
82
- quoted_right = quote_string(value.right)
83
- encryption_instruction = %[pgp_pub_encrypt('#{quoted_right}', #{key.dearmored})]
84
- value.right = Arel::Nodes::SqlLiteral.new(encryption_instruction)
85
- end
86
- end
87
- end
88
- end
@@ -1,24 +0,0 @@
1
- require 'arel/visitors/postgresql'
2
-
3
- # We override some fun stuff in the PostgreSQL visitor class inside of Arel.
4
- # This is the _most_ direct approach to tweaking the SQL to INSERT, SELECT,
5
- # and UPDATE values as encrypted. Unfortunately, the visitor API doesn't
6
- # give us access to managers as well as nodes, so we have use the public
7
- # Arel API via the connection adapter's to_sql method. Then we tweak the
8
- # more specific bits here!
9
-
10
- Arel::Visitors::PostgreSQL.class_eval do
11
- unless instance_methods.include?(:visit_Arel_Nodes_Assignment_without_pgcrypto) || instance_methods.include?('visit_Arel_Nodes_Assignment_without_pgcrypto')
12
- alias :visit_Arel_Nodes_Assignment_without_pgcrypto :visit_Arel_Nodes_Assignment
13
- end
14
-
15
- def visit_Arel_Nodes_Assignment(assignment)
16
- # Hijack the normally inoccuous assignment that happens, seeing as how
17
- # Arel normally forwards this shit to someone else and I hate it.
18
- if assignment.left.relation.name == PGCrypto::Column.table_name && assignment.left.name == 'value'
19
- "#{visit(assignment.left)} = #{visit(assignment.right)}"
20
- else
21
- visit_Arel_Nodes_Assignment_without_pgcrypto(assignment)
22
- end
23
- end
24
- end