pgcrypto 0.3.6 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +5 -1
- data/Gemfile +1 -4
- data/README.markdown +126 -33
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/pgcrypto_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb +20 -0
- data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb +22 -0
- data/lib/pgcrypto.rb +15 -105
- data/lib/pgcrypto/adapter.rb +162 -0
- data/lib/pgcrypto/column.rb +9 -8
- data/lib/pgcrypto/column_converter.rb +26 -0
- data/lib/pgcrypto/generators/base_generator.rb +12 -0
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/USAGE +0 -0
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/install_generator.rb +2 -5
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/templates/initializer.rb +0 -0
- data/lib/pgcrypto/generators/install/templates/migration.rb +5 -0
- data/lib/pgcrypto/generators/upgrade/USAGE +8 -0
- data/lib/pgcrypto/generators/upgrade/templates/migration.rb +22 -0
- data/lib/pgcrypto/generators/upgrade/upgrade_generator.rb +15 -0
- data/lib/pgcrypto/has_encrypted_column.rb +25 -0
- data/lib/pgcrypto/key.rb +0 -10
- data/lib/pgcrypto/key_manager.rb +11 -0
- data/lib/pgcrypto/railtie.rb +15 -0
- data/lib/pgcrypto/table.rb +11 -0
- data/lib/pgcrypto/table_manager.rb +2 -10
- data/lib/tasks/pgcrypto.rake +7 -0
- data/pgcrypto.gemspec +24 -17
- data/spec/lib/pgcrypto_spec.rb +101 -100
- data/spec/spec_helper.rb +27 -28
- metadata +20 -36
- data/lib/generators/pgcrypto/install/templates/migration.rb +0 -17
- data/lib/pgcrypto/active_record.rb +0 -88
- data/lib/pgcrypto/arel.rb +0 -24
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
5
|
-
require '
|
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
|
-
|
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 = {
|
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 :
|
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
|
-
|
58
|
+
has_encrypted_column :encrypted_text
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
64
|
-
config.after :
|
65
|
-
|
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.
|
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-
|
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/
|
75
|
-
- lib/
|
76
|
-
- lib/
|
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/
|
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
|
data/lib/pgcrypto/arel.rb
DELETED
@@ -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
|