hypershield 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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