grantinee 0.3.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.
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grantinee
4
+ module Engine
5
+ class AbstractEngine
6
+ NOT_IMPLEMENTED = "Not implemented".freeze
7
+
8
+ def logger
9
+ Grantinee.logger
10
+ end
11
+
12
+ def initialize
13
+ raise NOT_IMPLEMENTED
14
+ end
15
+
16
+ def flush_permissions!
17
+ raise NOT_IMPLEMENTED
18
+ end
19
+
20
+ def revoke_permissions!(_data)
21
+ raise NOT_IMPLEMENTED
22
+ end
23
+
24
+ def grant_permission!(_data)
25
+ raise NOT_IMPLEMENTED
26
+ end
27
+
28
+ def run!(_query, _data = {})
29
+ raise NOT_IMPLEMENTED
30
+ end
31
+
32
+ # Sanitize one value piece
33
+ def sanitize_value(_value)
34
+ raise NOT_IMPLEMENTED
35
+ end
36
+
37
+ # Sanitize column name
38
+ def sanitize_column_name(_name)
39
+ raise NOT_IMPLEMENTED
40
+ end
41
+
42
+ # Sanitize table name
43
+ def sanitize_table_name(_name)
44
+ raise NOT_IMPLEMENTED
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem 'mysql2', '>= 0.4.4', '< 0.6.0'
4
+ require 'mysql2'
5
+
6
+ module Grantinee
7
+ module Engine
8
+ class Mysql < AbstractEngine
9
+ def initialize
10
+ configuration = Grantinee.configuration
11
+
12
+ @connection = Mysql2::Client.new(
13
+ username: configuration.username,
14
+ password: configuration.password,
15
+ host: configuration.hostname,
16
+ port: configuration.port,
17
+ database: configuration.database
18
+ )
19
+ end
20
+
21
+ def flush_permissions!
22
+ query = "FLUSH PRIVILEGES;"
23
+
24
+ run! query
25
+ end
26
+
27
+ def revoke_permissions!(data)
28
+ database = sanitize_column_name(data[:database])
29
+ user = sanitize_value(data[:user])
30
+ host = sanitize_value(data[:host])
31
+
32
+ query = "REVOKE ALL PRIVILEGES ON #{database}.* FROM '#{user}'@'#{host}';"
33
+ run! query, data
34
+ end
35
+
36
+ def grant_permission!(data) # rubocop:disable Metrics/AbcSize
37
+ raise "Invalid permission kind" unless WHITELISTED_KINDS.include?(data[:kind])
38
+
39
+ database = sanitize_column_name(data[:database])
40
+ kind = data[:kind]
41
+ table = sanitize_table_name(data[:table])
42
+ user = sanitize_value(data[:user])
43
+ host = sanitize_value(data[:host])
44
+ fields = data[:fields].map { |v| sanitize_column_name(v.to_s) }.join(', ')
45
+
46
+ query = if data[:fields].empty?
47
+ "GRANT #{kind} ON #{database}.#{table} TO '#{user}'@'#{host}';"
48
+ else
49
+ "GRANT #{kind}(#{fields}) ON #{database}.#{table} TO '#{user}'@'#{host}';"
50
+ end
51
+ run! query, data
52
+ end
53
+
54
+ private
55
+
56
+ def sanitize_value(value)
57
+ @connection.escape value
58
+ end
59
+
60
+ def sanitize_column_name(name)
61
+ "`#{name.to_s.gsub('`', '``')}`"
62
+ end
63
+
64
+ def sanitize_table_name(name)
65
+ sanitize_column_name(name).gsub('.', '`.`')
66
+ end
67
+
68
+ def run!(query, data = {})
69
+ logger.info query
70
+
71
+ begin
72
+ @connection.query query
73
+ rescue ::Mysql2::Error => e
74
+ case e.error_number
75
+ when 1141, 1269 # Can't revoke all privileges for one or more of the requested users
76
+ logger.debug format("User %{user}@%{host} doesn't have any grants yet", data)
77
+ when 1133 # Can't find any matching row in the user table
78
+ logger.fatal format("User %{user}@%{host} doesn't exist yet, create it with \"CREATE USER '%{user}'@'%{host}';\" first", data) # rubocop:disable Metrics/LineLength
79
+ else
80
+ logger.debug e.error_number
81
+ raise e
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem 'pg', '>= 0.18', '< 2.0'
4
+ require 'pg'
5
+
6
+ module Grantinee
7
+ module Engine
8
+ class Postgresql < AbstractEngine
9
+ def initialize
10
+ configuration = Grantinee.configuration
11
+
12
+ @connection = PG::Connection.open(
13
+ user: configuration.username,
14
+ password: configuration.password,
15
+ host: configuration.hostname,
16
+ port: configuration.port,
17
+ dbname: configuration.database
18
+ )
19
+ end
20
+
21
+ def flush_permissions!
22
+ # Postgres doesn't need to flush privileges
23
+ end
24
+
25
+ def revoke_permissions!(data)
26
+ database = sanitize_column_name(data[:database])
27
+ user = sanitize_column_name(data[:database])
28
+
29
+ query = "REVOKE ALL PRIVILEGES ON DATABASE #{database} FROM #{user};"
30
+ run! query, data
31
+ end
32
+
33
+ def grant_permission!(data)
34
+ raise "Invalid permission kind" unless WHITELISTED_KINDS.include?(data[:kind])
35
+
36
+ kind = data[:kind]
37
+ table = sanitize_table_name(data[:table])
38
+ user = sanitize_column_name(data[:user])
39
+ fields = data[:fields].map { |v| sanitize_column_name(v.to_s) }.join(', ')
40
+
41
+ query = if data[:fields].empty?
42
+ "GRANT #{kind} ON #{table} TO #{user};"
43
+ else
44
+ "GRANT #{kind}(#{fields}) ON TABLE #{table} TO #{user};"
45
+ end
46
+ run! query, data
47
+ end
48
+
49
+ private
50
+
51
+ def sanitize_value(value)
52
+ @connection.escape_string value.to_s
53
+ end
54
+
55
+ def sanitize_column_name(name)
56
+ @connection.quote_ident name.to_s
57
+ end
58
+
59
+ def sanitize_table_name(name)
60
+ @connection.quote_ident name.to_s
61
+ end
62
+
63
+ def run!(query, data = {})
64
+ logger.info query
65
+
66
+ begin
67
+ @connection.exec query
68
+ rescue PG::Error => e
69
+ case e
70
+ when PG::UndefinedObject
71
+ logger.fatal format("User %{user}@%{host} doesn't exist yet, create it with \"CREATE ROLE %{user};\" first", data) # rubocop:disable Metrics/LineLength
72
+ else
73
+ logger.debug e.class
74
+ raise e
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grantinee
4
+ class Executor
5
+ def initialize(dsl, engine = Executor.default_engine)
6
+ @dsl = dsl
7
+ @engine = engine
8
+ end
9
+
10
+ def run!
11
+ revoke_permissions
12
+ grant_permissions
13
+ flush_permissions
14
+ end
15
+
16
+ def self.default_engine
17
+ Grantinee::Engine.for(Grantinee.configuration.engine)
18
+ end
19
+
20
+ private
21
+
22
+ def revoke_permissions
23
+ @dsl.permissions.each { |data| @engine.revoke_permissions!(data) }
24
+ end
25
+
26
+ def grant_permissions
27
+ @dsl.permissions.each { |data| @engine.grant_permission!(data) }
28
+ end
29
+
30
+ def flush_permissions
31
+ @engine.flush_permissions!
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grantinee
4
+ VERSION = '0.3.1'.freeze
5
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grantinee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Paweł Komarnicki
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: method_source
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: A Ruby library to manage your database permissions for MySQL and PostgreSQL.
98
+ Supports per-table, and per-column permissions for granular access and security.
99
+ email:
100
+ - pawel@blinkist.com
101
+ executables:
102
+ - grantinee
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".circleci/config.yml"
107
+ - ".circleci/setup-rubygems.sh"
108
+ - ".gitignore"
109
+ - ".rspec"
110
+ - ".rubocop.yml"
111
+ - ".travis.yml"
112
+ - CODE_OF_CONDUCT.md
113
+ - Dockerfile
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - Grantinee
117
+ - LICENSE.txt
118
+ - README.md
119
+ - Rakefile
120
+ - bin/console
121
+ - bin/setup
122
+ - config/grantinee.rb
123
+ - docker-compose.yml
124
+ - exe/grantinee
125
+ - grantinee.gemspec
126
+ - lib/grantinee.rb
127
+ - lib/grantinee/cli.rb
128
+ - lib/grantinee/configuration.rb
129
+ - lib/grantinee/dsl.rb
130
+ - lib/grantinee/engine.rb
131
+ - lib/grantinee/engine/abstract_engine.rb
132
+ - lib/grantinee/engine/mysql.rb
133
+ - lib/grantinee/engine/postgresql.rb
134
+ - lib/grantinee/executor.rb
135
+ - lib/grantinee/version.rb
136
+ homepage: https://github.com/blinkist/grantinee
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.5.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: '"Your permissions, freshly baked!" | A library to manage your database permissions
160
+ for MySQL and Postgres'
161
+ test_files: []