hypershield 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1222f7f264c0a8892096edecd7b6cc71a79fb2f1a7c81f7f5be42ecc9c19291
4
- data.tar.gz: e661dabb10c803a25a71effde3b70dab5ff26c7a4bfe470f2f8fdcba9a7fb4a5
3
+ metadata.gz: 00b099534fb260d68ee6bbca50a415d78f82d7fe9024775d27b49ead519b1e52
4
+ data.tar.gz: 9bb5c84b8a84404f787f385c141fcfb862a41df9a76366f9d7dbb3c2b762c352
5
5
  SHA512:
6
- metadata.gz: '078a50cf334a8b95d9355a2265f15f2633dacc16be8f560bbdec9dcae346d90ef308581dfe8d6fdc76e9b8975bc9f000dfd062e153a80277d3784a5c6bee6a99'
7
- data.tar.gz: 3feac1364504a11be41725e84913d48fc418ab13fa8082feabf9091e38b658a208461519f9956f2a9728105aa89c5af9a7dd04872c45bd2b045bca2eaf790457
6
+ metadata.gz: 192f7058ccaf64897274e24ceffef1623fb192aae272f8930a5681ed7f6a07f0bb5161964b9eb2cc278415c35c70e831cede4a58d332f476610c44720533c436
7
+ data.tar.gz: 9268101459ca8d4f45c51e9be2dec4b60786c22d5c2fd69d83bd7d0f7174ac5f18ae3f308aeb949146cdb189563db8fe7fb3c7b20d569c21fe59927ea27e4ff2
@@ -1,8 +1,15 @@
1
- ## 0.1.1
1
+ ## 0.2.0 (2020-03-12)
2
+
3
+ - Only update views that have changed
4
+ - Added `hypershield:refresh:dry_run` rake task
5
+ - Added `enabled` option
6
+ - Added generator
7
+
8
+ ## 0.1.1 (2019-09-20)
2
9
 
3
10
  - Fixed error with MySQL 8
4
11
  - Added `log_sql` option
5
12
 
6
- ## 0.1.0
13
+ ## 0.1.0 (2018-04-01)
7
14
 
8
15
  - First release
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2019 Andrew Kane
3
+ Copyright (c) 2018-2020 Andrew Kane
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -19,6 +19,39 @@ By default, it hides columns with:
19
19
 
20
20
  Give database users access to these views instead of the original tables.
21
21
 
22
+ ## Installation
23
+
24
+ Add this line to your application’s Gemfile:
25
+
26
+ ```ruby
27
+ gem 'hypershield'
28
+ ```
29
+
30
+ And run:
31
+
32
+ ```sh
33
+ rails generate hypershield:install
34
+ ```
35
+
36
+ Hypershield is disabled in non-production environments by default. You can do a dry run with:
37
+
38
+ ```sh
39
+ rake hypershield:refresh:dry_run
40
+ ```
41
+
42
+ Next, set up your production database.
43
+
44
+ - [Postgres](#postgres)
45
+ - [MySQL](#mysql)
46
+
47
+ When that’s done, deploy to production and run:
48
+
49
+ ```sh
50
+ rails db:migrate
51
+ ```
52
+
53
+ The schema will automatically refresh.
54
+
22
55
  ## Database Setup
23
56
 
24
57
  ### Postgres
@@ -69,50 +102,32 @@ And connect as the user and make sure there’s no access the original tables
69
102
  SELECT * FROM mydb.users LIMIT 1;
70
103
  ```
71
104
 
72
- ## Installation
73
-
74
- Add this line to your application’s Gemfile:
75
-
76
- ```ruby
77
- gem 'hypershield', group: :production
78
- ```
79
-
80
- Refresh the schema
81
-
82
- ```sh
83
- rake hypershield:refresh
84
- ```
85
-
86
- And query away on your shielded views
87
-
88
- ```sql
89
- SELECT * FROM users LIMIT 1;
90
- ```
91
-
92
- When you run database migrations, the schema is automatically refreshed.
93
-
94
105
  ## Configuration
95
106
 
107
+ Set configuration in `config/initializers/hypershield.rb`.
108
+
96
109
  Specify the schema to use and columns to show and hide
97
110
 
98
111
  ```ruby
99
112
  Hypershield.schemas = {
100
113
  hypershield: {
101
- hide: %w(encrypted password token secret),
102
- show: %w(ahoy_visits.visit_token)
114
+ hide: ["encrypted", "password", "token", "secret"],
115
+ show: ["ahoy_visits.visitor_token", "ahoy_visits.visit_token"]
103
116
  }
104
117
  }
105
118
  ```
106
119
 
107
- Log Hypershield SQL statements [master]
120
+ Log Hypershield SQL statements
108
121
 
109
122
  ```ruby
110
123
  Hypershield.log_sql = true
111
124
  ```
112
125
 
113
- ## TODO
126
+ Enable or disable Hypershield in an environment
114
127
 
115
- - Create CLI
128
+ ```ruby
129
+ Hypershield.enabled = Rails.env.production?
130
+ ```
116
131
 
117
132
  ## History
118
133
 
@@ -126,3 +141,11 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
126
141
  - Fix bugs and [submit pull requests](https://github.com/ankane/hypershield/pulls)
127
142
  - Write, clarify, or fix documentation
128
143
  - Suggest or add new features
144
+
145
+ To get started with development:
146
+
147
+ ```sh
148
+ git clone https://github.com/ankane/hypershield.git
149
+ cd hypershield
150
+ bundle install
151
+ ```
@@ -0,0 +1,13 @@
1
+ require "rails/generators"
2
+
3
+ module Hypershield
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.join(__dir__, "templates")
7
+
8
+ def create_initializer
9
+ template "initializer.rb", "config/initializers/hypershield.rb"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # Specify which environments to use Hypershield
2
+ Hypershield.enabled = Rails.env.production?
3
+
4
+ # Specify the schema to use and columns to show and hide
5
+ Hypershield.schemas = {
6
+ hypershield: {
7
+ # columns to hide
8
+ # matches table.column
9
+ hide: ["encrypted", "password", "token", "secret"],
10
+ # overrides hide
11
+ # matches table.column
12
+ show: []
13
+ }
14
+ }
15
+
16
+ # Log SQL statements
17
+ Hypershield.log_sql = false
@@ -10,15 +10,16 @@ require "hypershield/engine" if defined?(Rails)
10
10
 
11
11
  module Hypershield
12
12
  class << self
13
- attr_accessor :schemas, :log_sql
13
+ attr_accessor :enabled, :log_sql, :schemas
14
14
  end
15
+ self.enabled = true
16
+ self.log_sql = false
15
17
  self.schemas = {
16
18
  hypershield: {
17
19
  hide: %w(encrypted password token secret),
18
20
  show: []
19
21
  }
20
22
  }
21
- self.log_sql = false
22
23
 
23
24
  class << self
24
25
  def drop_view(view)
@@ -27,7 +28,7 @@ module Hypershield
27
28
  end
28
29
  end
29
30
 
30
- def refresh
31
+ def refresh(dry_run: false)
31
32
  if adapter_name =~ /sqlite/i
32
33
  raise "Adapter not supported: #{adapter_name}"
33
34
  end
@@ -39,31 +40,40 @@ module Hypershield
39
40
  hide = config[:hide].to_a
40
41
  show = config[:show].to_a
41
42
 
43
+ hypershield_tables = tables(schema)
44
+
42
45
  tables.sort_by { |k, _| k }.each do |table, columns|
43
46
  next if table == "pg_stat_statements"
44
47
 
45
- statements << "DROP VIEW IF EXISTS #{quote_ident(schema)}.#{quote_ident(table)} CASCADE"
46
-
47
48
  columns.reject! do |column|
48
49
  hide.any? { |m| "#{table}.#{column}".include?(m) } &&
49
50
  !show.any? { |m| "#{table}.#{column}".include?(m) }
50
51
  end
51
52
 
53
+ # if the hypershield view has the same columns, assume it doesn't need updated
54
+ # this may not necessarily be true if someone manually updates the view
55
+ # we could check the view definition, but this is harder as the database normalizes it
56
+ next if hypershield_tables[table] == columns
57
+
58
+ statements << "DROP VIEW IF EXISTS #{quote_ident(schema)}.#{quote_ident(table)} CASCADE"
59
+
52
60
  if columns.any?
53
61
  statements << "CREATE VIEW #{quote_ident(schema)}.#{quote_ident(table)} AS SELECT #{columns.map { |c| quote_ident(c) }.join(", ")} FROM #{quote_ident(table)}"
54
62
  end
55
63
  end
56
64
  end
57
65
 
58
- if statements.any?
59
- connection.transaction do
60
- if mysql?
61
- statements.each do |statement|
62
- execute(statement)
63
- end
64
- else
65
- execute(statements.join(";\n"))
66
- end
66
+ if dry_run
67
+ if statements.any?
68
+ puts statements.map { |v| "#{v};" }.join("\n")
69
+ end
70
+ else
71
+ # originally this was performed in a transaction
72
+ # however, this appears to cause issues in certain situations - see #5 and #6
73
+ # this shouldn't be a huge issue now that we only update specific views
74
+ # we already drop views outside of the transaction when migrations are run
75
+ statements.each do |statement|
76
+ execute(statement)
67
77
  end
68
78
  end
69
79
  end
@@ -97,14 +107,17 @@ module Hypershield
97
107
  adapter_name =~ /mysql/i
98
108
  end
99
109
 
100
- def tables
101
- # TODO make schema configurable
102
- schema =
103
- if mysql?
104
- "database()"
105
- else
106
- "'public'"
107
- end
110
+ def tables(schema = nil)
111
+ if schema
112
+ schema = quote(schema)
113
+ else
114
+ schema =
115
+ if mysql?
116
+ "database()"
117
+ else
118
+ "'public'"
119
+ end
120
+ end
108
121
 
109
122
  query = <<-SQL
110
123
  SELECT
@@ -133,6 +146,10 @@ module Hypershield
133
146
  connection.execute(sql)
134
147
  end
135
148
 
149
+ def quote(literal)
150
+ connection.quote(literal)
151
+ end
152
+
136
153
  def quote_ident(ident)
137
154
  connection.quote_table_name(ident.to_s)
138
155
  end
@@ -1,3 +1,3 @@
1
1
  module Hypershield
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,15 +1,23 @@
1
1
  namespace :hypershield do
2
2
  task refresh: :environment do
3
+ abort "Hypershield is not enabled in this environment. Do a dry run with: rake hypershield:refresh:dry_run" unless Hypershield.enabled
4
+
3
5
  $stderr.puts "[hypershield] Refreshing schemas"
4
6
  Hypershield.refresh
5
7
  $stderr.puts "[hypershield] Success!"
6
8
  end
9
+
10
+ namespace :refresh do
11
+ task dry_run: :environment do
12
+ Hypershield.refresh(dry_run: true)
13
+ end
14
+ end
7
15
  end
8
16
 
9
17
  Rake::Task["db:migrate"].enhance do
10
- Rake::Task["hypershield:refresh"].invoke
18
+ Rake::Task["hypershield:refresh"].invoke if Hypershield.enabled
11
19
  end
12
20
 
13
21
  Rake::Task["db:rollback"].enhance do
14
- Rake::Task["hypershield:refresh"].invoke
22
+ Rake::Task["hypershield:refresh"].invoke if Hypershield.enabled
15
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypershield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-20 00:00:00.000000000 Z
11
+ date: 2020-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -117,6 +117,8 @@ files:
117
117
  - CHANGELOG.md
118
118
  - LICENSE.txt
119
119
  - README.md
120
+ - lib/generators/hypershield/install_generator.rb
121
+ - lib/generators/hypershield/templates/initializer.rb
120
122
  - lib/hypershield.rb
121
123
  - lib/hypershield/engine.rb
122
124
  - lib/hypershield/migration.rb
@@ -141,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
143
  - !ruby/object:Gem::Version
142
144
  version: '0'
143
145
  requirements: []
144
- rubygems_version: 3.0.3
146
+ rubygems_version: 3.1.2
145
147
  signing_key:
146
148
  specification_version: 4
147
149
  summary: Shield sensitive data in Postgres and MySQL