posgra 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/posgra +15 -0
- data/lib/posgra.rb +33 -0
- data/lib/posgra/cli.rb +6 -0
- data/lib/posgra/cli/app.rb +16 -0
- data/lib/posgra/cli/grant.rb +68 -0
- data/lib/posgra/cli/helper.rb +37 -0
- data/lib/posgra/cli/role.rb +35 -0
- data/lib/posgra/client.rb +247 -0
- data/lib/posgra/driver.rb +354 -0
- data/lib/posgra/dsl.rb +17 -0
- data/lib/posgra/dsl/converter.rb +136 -0
- data/lib/posgra/dsl/grants.rb +53 -0
- data/lib/posgra/dsl/grants/role.rb +23 -0
- data/lib/posgra/dsl/grants/role/schema.rb +22 -0
- data/lib/posgra/dsl/grants/role/schema/on.rb +22 -0
- data/lib/posgra/dsl/roles.rb +67 -0
- data/lib/posgra/dsl/roles/group.rb +24 -0
- data/lib/posgra/exporter.rb +25 -0
- data/lib/posgra/ext/string_ext.rb +25 -0
- data/lib/posgra/identifier.rb +2 -0
- data/lib/posgra/identifier/auto.rb +38 -0
- data/lib/posgra/logger.rb +33 -0
- data/lib/posgra/template.rb +18 -0
- data/lib/posgra/utils.rb +17 -0
- data/lib/posgra/version.rb +3 -0
- data/posgra.gemspec +32 -0
- metadata +206 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 779439047ffa052724a0188fddd2d07291d632be
|
4
|
+
data.tar.gz: dc13051ea5f743c90bb03f17e8552b8042403804
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 969080594fab8c8fd0f4b99d7c7188ab52e15e9147a51caf55c41df74bc9485245460439639cde467e3f7fa491eca91382630e5983b9f884af162b5e877f44dd
|
7
|
+
data.tar.gz: 685cbe3e19daba11a16d2f4852ccab18794c4eebb8c183128ee0428e557a34d66f0b91ddb724bad9bc173aadd90dcdf0293fd7f154a88dbb1b9e3ba6afea4d88
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.8
|
6
|
+
- 2.2.4
|
7
|
+
- 2.3.0
|
8
|
+
before_install: gem install bundler -v 1.11.2
|
9
|
+
script:
|
10
|
+
- bundle install
|
11
|
+
- bundle exec rake
|
12
|
+
addons:
|
13
|
+
postgresql: "9.4"
|
14
|
+
env:
|
15
|
+
global:
|
16
|
+
- POSGRA_TEST_USER=postgres
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Genki Sugawara
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Posgra
|
2
|
+
|
3
|
+
Posgra is a tool to manage PostgreSQL roles/permissions.
|
4
|
+
|
5
|
+
It defines the state of PostgreSQL roles/permissions using Ruby DSL, and updates roles/permissions according to DSL.
|
6
|
+
|
7
|
+
[![Build Status](https://travis-ci.org/winebarrel/posgra.svg?branch=master)](https://travis-ci.org/winebarrel/posgra)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'posgra'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install posgra
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
```sh
|
28
|
+
$ posgra help
|
29
|
+
Commands:
|
30
|
+
posgra grant SUBCOMMAND # Manage grants
|
31
|
+
posgra help [COMMAND] # Describe available commands or one specific command
|
32
|
+
posgra role SUBCOMMAND # Manage roles
|
33
|
+
|
34
|
+
Options:
|
35
|
+
-h, [--host=HOST]
|
36
|
+
# Default: localhost
|
37
|
+
-p, [--port=N]
|
38
|
+
# Default: 5432
|
39
|
+
-d, [--dbname=DBNAME]
|
40
|
+
# Default: postgres
|
41
|
+
-U, [--user=USER]
|
42
|
+
-P, [--password=PASSWORD]
|
43
|
+
[--account-output=ACCOUNT-OUTPUT]
|
44
|
+
# Default: account.csv
|
45
|
+
[--color], [--no-color]
|
46
|
+
# Default: true
|
47
|
+
[--debug], [--no-debug]
|
48
|
+
```
|
49
|
+
|
50
|
+
```sh
|
51
|
+
posgra role export pg_roles.rb
|
52
|
+
vi pg_roles.rb
|
53
|
+
posgra role apply --dry-run pg_roles.rb
|
54
|
+
posgra role apply pg_roles.rb
|
55
|
+
```
|
56
|
+
|
57
|
+
```sh
|
58
|
+
posgra grant export pg_grants.rb
|
59
|
+
vi pg_grants.rb
|
60
|
+
posgra grant apply --dry-run pg_grants.rb
|
61
|
+
posgra grant apply pg_grants.rb
|
62
|
+
```
|
63
|
+
|
64
|
+
## DSL Example
|
65
|
+
|
66
|
+
### Role
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
user "alice"
|
70
|
+
|
71
|
+
group "staff" do
|
72
|
+
user "bob"
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
### Grant
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
role "bob" do
|
80
|
+
schema "main" do
|
81
|
+
on "microposts" do
|
82
|
+
grant "DELETE", grantable: true
|
83
|
+
grant "INSERT"
|
84
|
+
grant "REFERENCES"
|
85
|
+
grant "SELECT"
|
86
|
+
grant "TRIGGER"
|
87
|
+
grant "TRUNCATE"
|
88
|
+
grant "UPDATE"
|
89
|
+
end
|
90
|
+
on "microposts_id_seq" do
|
91
|
+
grant "SELECT"
|
92
|
+
grant "UPDATE"
|
93
|
+
end
|
94
|
+
on /^user/ do
|
95
|
+
grant "SELECT"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "posgra"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/posgra
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'posgra'
|
4
|
+
|
5
|
+
debug = ARGV.any? {|i| i == '--debug' }
|
6
|
+
|
7
|
+
begin
|
8
|
+
Posgra::CLI::App.start(ARGV)
|
9
|
+
rescue => e
|
10
|
+
if debug
|
11
|
+
raise e
|
12
|
+
else
|
13
|
+
$stderr.puts "ERROR: #{e}".red
|
14
|
+
end
|
15
|
+
end
|
data/lib/posgra.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'logger'
|
3
|
+
require 'pg'
|
4
|
+
require 'singleton'
|
5
|
+
require 'term/ansicolor'
|
6
|
+
require 'thor'
|
7
|
+
|
8
|
+
module Posgra; end
|
9
|
+
|
10
|
+
require 'posgra/ext/string_ext'
|
11
|
+
require 'posgra/logger'
|
12
|
+
require 'posgra/template'
|
13
|
+
require 'posgra/utils'
|
14
|
+
require 'posgra/cli'
|
15
|
+
require 'posgra/cli'
|
16
|
+
require 'posgra/cli/helper'
|
17
|
+
require 'posgra/cli/grant'
|
18
|
+
require 'posgra/cli/role'
|
19
|
+
require 'posgra/cli/app'
|
20
|
+
require 'posgra/client'
|
21
|
+
require 'posgra/driver'
|
22
|
+
require 'posgra/dsl'
|
23
|
+
require 'posgra/dsl/grants'
|
24
|
+
require 'posgra/dsl/grants/role'
|
25
|
+
require 'posgra/dsl/grants/role/schema'
|
26
|
+
require 'posgra/dsl/grants/role/schema/on'
|
27
|
+
require 'posgra/dsl/roles'
|
28
|
+
require 'posgra/dsl/roles/group'
|
29
|
+
require 'posgra/dsl/converter'
|
30
|
+
require 'posgra/exporter'
|
31
|
+
require 'posgra/identifier'
|
32
|
+
require 'posgra/identifier/auto'
|
33
|
+
require 'posgra/version'
|
data/lib/posgra/cli.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Posgra::CLI::App < Thor
|
2
|
+
class_option :host, :default => 'localhost', :aliases => '-h'
|
3
|
+
class_option :port, :type => :numeric, :default => 5432, :aliases => '-p'
|
4
|
+
class_option :dbname, :default => 'postgres', :aliases => '-d'
|
5
|
+
class_option :user, :aliases => '-U'
|
6
|
+
class_option :password, :aliases => '-P'
|
7
|
+
class_option :'account-output', :default => 'account.csv'
|
8
|
+
class_option :color, :type => :boolean, :default => true
|
9
|
+
class_option :debug, :type => :boolean, :default => false
|
10
|
+
|
11
|
+
desc 'role SUBCOMMAND', 'Manage roles'
|
12
|
+
subcommand :role, Posgra::CLI::Role
|
13
|
+
|
14
|
+
desc 'grant SUBCOMMAND', 'Manage grants'
|
15
|
+
subcommand :grant, Posgra::CLI::Grant
|
16
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Posgra::CLI::Grant < Thor
|
2
|
+
include Posgra::CLI::Helper
|
3
|
+
include Posgra::Logger::Helper
|
4
|
+
|
5
|
+
DEFAULT_FILENAME = 'pg_grants.rb'
|
6
|
+
|
7
|
+
class_option :'include-schema'
|
8
|
+
class_option :'exclude-schema'
|
9
|
+
class_option :'include-role'
|
10
|
+
class_option :'exclude-role'
|
11
|
+
|
12
|
+
desc 'apply FILE', 'Apply grants'
|
13
|
+
option :'dry-run', :type => :boolean, :default => false
|
14
|
+
def apply(file)
|
15
|
+
check_fileanem(file)
|
16
|
+
updated = client.apply_grants(file)
|
17
|
+
|
18
|
+
unless updated
|
19
|
+
Posgra::Logger.instance.info('No change'.intense_blue)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'export [FILE]', 'Export grants'
|
24
|
+
option :split, :type => :boolean, :default => false
|
25
|
+
def export(file = nil)
|
26
|
+
check_fileanem(file)
|
27
|
+
dsl = client.export_grants
|
28
|
+
|
29
|
+
if options[:split]
|
30
|
+
file = DEFAULT_FILENAME unless file
|
31
|
+
|
32
|
+
log(:info, 'Export Grants')
|
33
|
+
requires = []
|
34
|
+
|
35
|
+
dsl.each do |user, content|
|
36
|
+
grant_file = "#{user}.rb"
|
37
|
+
requires << grant_file
|
38
|
+
log(:info, " write `#{grant_file}`")
|
39
|
+
|
40
|
+
open(grant_file, 'wb') do |f|
|
41
|
+
f.puts Posgra::CLI::MAGIC_COMMENT
|
42
|
+
f.puts content
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
log(:info, " write `#{file}`")
|
47
|
+
|
48
|
+
open(file, 'wb') do |f|
|
49
|
+
f.puts Posgra::CLI::MAGIC_COMMENT
|
50
|
+
|
51
|
+
requires.each do |grant_file|
|
52
|
+
f.puts "require '#{File.basename grant_file}'"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
else
|
56
|
+
if file.nil? or file == '-'
|
57
|
+
puts dsl
|
58
|
+
else
|
59
|
+
log(:info, "Export Grants to `#{file}`")
|
60
|
+
|
61
|
+
open(file, 'wb') do |f|
|
62
|
+
f.puts Posgra::CLI::MAGIC_COMMENT
|
63
|
+
f.puts dsl
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Posgra::CLI::Helper
|
2
|
+
REGEXP_OPTIONS = [
|
3
|
+
:include_schema,
|
4
|
+
:exclude_schema,
|
5
|
+
:include_role,
|
6
|
+
:exclude_role,
|
7
|
+
]
|
8
|
+
|
9
|
+
def check_fileanem(file)
|
10
|
+
if file =~ /\A-.+/
|
11
|
+
raise "Invalid failname: #{file}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def client
|
16
|
+
client_options = {}
|
17
|
+
String.colorize = options[:color]
|
18
|
+
Posgra::Logger.instance.set_debug(options[:debug])
|
19
|
+
|
20
|
+
options.each do |key, value|
|
21
|
+
if key.to_s =~ /-/
|
22
|
+
key = key.to_s.gsub('-', '_')
|
23
|
+
end
|
24
|
+
|
25
|
+
client_options[key.to_sym] = value if value
|
26
|
+
end
|
27
|
+
|
28
|
+
REGEXP_OPTIONS.each do |key|
|
29
|
+
if client_options[key]
|
30
|
+
client_options[key] = Regexp.new(client_options[key])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
client_options[:identifier] = Posgra::Identifier::Auto.new(options['account-output'])
|
35
|
+
Posgra::Client.new(client_options)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Posgra::CLI::Role < Thor
|
2
|
+
include Posgra::CLI::Helper
|
3
|
+
include Posgra::Logger::Helper
|
4
|
+
|
5
|
+
class_option :'include-role'
|
6
|
+
class_option :'exclude-role'
|
7
|
+
|
8
|
+
desc 'apply FILE', 'Apply roles'
|
9
|
+
option :'dry-run', :type => :boolean, :default => false
|
10
|
+
def apply(file)
|
11
|
+
check_fileanem(file)
|
12
|
+
updated = client.apply_roles(file)
|
13
|
+
|
14
|
+
unless updated
|
15
|
+
Posgra::Logger.instance.info('No change'.intense_blue)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'export [FILE]', 'Export roles'
|
20
|
+
def export(file = nil)
|
21
|
+
check_fileanem(file)
|
22
|
+
dsl = client.export_roles
|
23
|
+
|
24
|
+
if file.nil? or file == '-'
|
25
|
+
puts dsl
|
26
|
+
else
|
27
|
+
log(:info, "Export Roles to `#{file}`")
|
28
|
+
|
29
|
+
open(file, 'wb') do |f|
|
30
|
+
f.puts Posgra::CLI::MAGIC_COMMENT
|
31
|
+
f.puts dsl
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
class Posgra::Client
|
2
|
+
DEFAULT_EXCLUDE_SCHEMA = /\A(?:pg_.*|information_schema)\z/
|
3
|
+
DEFAULT_EXCLUDE_ROLE = /\A\z/
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
if options[:exclude_schema]
|
7
|
+
options[:exclude_schema] = Regexp.union(
|
8
|
+
options[:exclude_schema],
|
9
|
+
DEFAULT_EXCLUDE_SCHEMA
|
10
|
+
)
|
11
|
+
else
|
12
|
+
options[:exclude_schema] = DEFAULT_EXCLUDE_SCHEMA
|
13
|
+
end
|
14
|
+
|
15
|
+
if options[:exclude_role]
|
16
|
+
options[:exclude_role] = Regexp.union(
|
17
|
+
options[:exclude_role],
|
18
|
+
DEFAULT_EXCLUDE_SCHEMA
|
19
|
+
)
|
20
|
+
else
|
21
|
+
options[:exclude_role] = DEFAULT_EXCLUDE_ROLE
|
22
|
+
end
|
23
|
+
|
24
|
+
@options = options
|
25
|
+
@client = connect(options)
|
26
|
+
@driver = Posgra::Driver.new(@client, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def export_roles(options = {})
|
30
|
+
options = @options.merge(options)
|
31
|
+
exported = Posgra::Exporter.export_roles(@driver, options)
|
32
|
+
Posgra::DSL.convert_roles(exported, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def export_grants(options = {})
|
36
|
+
options = @options.merge(options)
|
37
|
+
exported = Posgra::Exporter.export_grants(@driver, options)
|
38
|
+
|
39
|
+
if options[:split]
|
40
|
+
dsl_h = Hash.new {|hash, key| hash[key] = {} }
|
41
|
+
|
42
|
+
|
43
|
+
exported.each do |role, schemas|
|
44
|
+
dsl = Posgra::DSL.convert_grants({role => schemas}, options)
|
45
|
+
dsl_h[role] = dsl
|
46
|
+
end
|
47
|
+
|
48
|
+
dsl_h
|
49
|
+
else
|
50
|
+
Posgra::DSL.convert_grants(exported, options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply_roles(file, options = {})
|
55
|
+
options = @options.merge(options)
|
56
|
+
walk_for_roles(file, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def apply_grants(file, options = {})
|
60
|
+
options = @options.merge(options)
|
61
|
+
walk_for_grants(file, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def close
|
65
|
+
@client.close
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def walk_for_roles(file, options)
|
71
|
+
expected = load_file(file, :parse_roles, options)
|
72
|
+
actual = Posgra::Exporter.export_roles(@driver, options)
|
73
|
+
|
74
|
+
expected_users_by_group = expected.fetch(:users_by_group)
|
75
|
+
actual_users_by_group = actual.fetch(:users_by_group)
|
76
|
+
expected_users = (expected_users_by_group.values.flatten + expected.fetch(:users)).uniq
|
77
|
+
actual_users = actual.fetch(:users)
|
78
|
+
|
79
|
+
updated = pre_walk_groups(expected_users_by_group, actual_users_by_group)
|
80
|
+
updated = walk_users(expected_users, actual_users) || updated
|
81
|
+
walk_groups(expected_users_by_group, actual_users_by_group, expected_users) || updated
|
82
|
+
end
|
83
|
+
|
84
|
+
def walk_for_grants(file, options)
|
85
|
+
expected = load_file(file, :parse_grants, options)
|
86
|
+
actual = Posgra::Exporter.export_grants(@driver, options)
|
87
|
+
walk_roles(expected, actual)
|
88
|
+
end
|
89
|
+
|
90
|
+
def walk_users(expected, actual)
|
91
|
+
updated = false
|
92
|
+
|
93
|
+
(expected - actual).each do |user|
|
94
|
+
updated = @driver.create_user(user) || updated
|
95
|
+
end
|
96
|
+
|
97
|
+
(actual - expected).each do |user|
|
98
|
+
updated = @driver.drop_user(user) || updated
|
99
|
+
end
|
100
|
+
|
101
|
+
updated
|
102
|
+
end
|
103
|
+
|
104
|
+
def pre_walk_groups(expected, actual)
|
105
|
+
updated = false
|
106
|
+
|
107
|
+
actual.reject {|group, _|
|
108
|
+
expected.has_key?(group)
|
109
|
+
}.each {|group, _|
|
110
|
+
updated = @driver.drop_group(group) || updated
|
111
|
+
}
|
112
|
+
|
113
|
+
updated
|
114
|
+
end
|
115
|
+
|
116
|
+
def walk_groups(expected, actual, expected_users)
|
117
|
+
updated = false
|
118
|
+
|
119
|
+
expected.each do |expected_group, expected_users|
|
120
|
+
actual_users = actual.delete(expected_group)
|
121
|
+
|
122
|
+
unless actual_users
|
123
|
+
updated = @driver.create_group(expected_group) || updated
|
124
|
+
actual_users = []
|
125
|
+
end
|
126
|
+
|
127
|
+
(expected_users - actual_users).each do |user|
|
128
|
+
updated = @driver.add_user_to_group(user, expected_group) || updated
|
129
|
+
end
|
130
|
+
|
131
|
+
(actual_users - expected_users).each do |user|
|
132
|
+
if expected_users.include?(user)
|
133
|
+
updated = @driver.drop_user_from_group(user, expected_group) || updated
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
updated
|
139
|
+
end
|
140
|
+
|
141
|
+
def walk_roles(expected, actual)
|
142
|
+
updated = false
|
143
|
+
|
144
|
+
expected.each do |expected_role, expected_schemas|
|
145
|
+
actual_schemas = actual.delete(expected_role) || {}
|
146
|
+
updated = walk_schemas(expected_schemas, actual_schemas, expected_role) || updated
|
147
|
+
end
|
148
|
+
|
149
|
+
actual.each do |actual_role, actual_schemas|
|
150
|
+
actual_schemas.each do |schema, _|
|
151
|
+
updated = @driver.revoke_all_on_schema(actual_role, schema) || updated
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
updated
|
156
|
+
end
|
157
|
+
|
158
|
+
def walk_schemas(expected, actual, role)
|
159
|
+
updated = false
|
160
|
+
|
161
|
+
expected.each do |expected_schema, expected_objects|
|
162
|
+
actual_objects = actual.delete(expected_schema) || {}
|
163
|
+
updated = walk_objects(expected_objects, actual_objects, role, expected_schema) || updated
|
164
|
+
end
|
165
|
+
|
166
|
+
actual.each do |actual_schema, _|
|
167
|
+
updated = @driver.revoke_all_on_schema(role, actual_schema) || updated
|
168
|
+
end
|
169
|
+
|
170
|
+
updated
|
171
|
+
end
|
172
|
+
|
173
|
+
def walk_objects(expected, actual, role, schema)
|
174
|
+
updated = false
|
175
|
+
|
176
|
+
expected.keys.each do |expected_object|
|
177
|
+
if expected_object.is_a?(Regexp)
|
178
|
+
expected_grants = expected.delete(expected_object)
|
179
|
+
|
180
|
+
@driver.describe_objects(schema).each do |object|
|
181
|
+
if object =~ expected_object
|
182
|
+
expected[object] = expected_grants.dup
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
expected.each do |expected_object, expected_grants|
|
189
|
+
actual_grants = actual.delete(expected_object) || {}
|
190
|
+
updated = walk_grants(expected_grants, actual_grants, role, schema, expected_object) || updated
|
191
|
+
end
|
192
|
+
|
193
|
+
actual.each do |actual_object, _|
|
194
|
+
updated = @driver.revoke_all_on_object(role, schema, actual_object) || updated
|
195
|
+
end
|
196
|
+
|
197
|
+
updated
|
198
|
+
end
|
199
|
+
|
200
|
+
def walk_grants(expected, actual, role, schema, object)
|
201
|
+
updated = false
|
202
|
+
|
203
|
+
expected.each do |expected_priv, expected_options|
|
204
|
+
actual_options = actual.delete(expected_priv)
|
205
|
+
|
206
|
+
if actual_options
|
207
|
+
if expected_options != actual_options
|
208
|
+
updated = @driver.update_grant_options(role, expected_priv, expected_options, schema, object) || updated
|
209
|
+
end
|
210
|
+
else
|
211
|
+
updated = @driver.grant(role, expected_priv, expected_options, schema, object) || updated
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
actual.each do |actual_priv, _|
|
216
|
+
updated = @driver.revoke(role, actual_priv, schema, object) || updated
|
217
|
+
end
|
218
|
+
|
219
|
+
updated
|
220
|
+
end
|
221
|
+
|
222
|
+
def load_file(file, method, options)
|
223
|
+
if file.kind_of?(String)
|
224
|
+
open(file) do |f|
|
225
|
+
Posgra::DSL.send(method, f.read, file, options)
|
226
|
+
end
|
227
|
+
elsif file.respond_to?(:read)
|
228
|
+
Posgra::DSL.send(method, file.read, file.path, options)
|
229
|
+
else
|
230
|
+
raise TypeError, "can't convert #{file} into File"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def connect(options)
|
235
|
+
connect_options = {}
|
236
|
+
|
237
|
+
PG::Connection::CONNECT_ARGUMENT_ORDER.each do |key|
|
238
|
+
value = options[key] || options[key.to_sym]
|
239
|
+
|
240
|
+
if value
|
241
|
+
connect_options[key] = value
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
PGconn.connect(connect_options)
|
246
|
+
end
|
247
|
+
end
|