lib/pg_ldap_sync/application.rb in pg-ldap-sync-0.1.1 vs lib/pg_ldap_sync/application.rb in pg-ldap-sync-0.2.0
- old
+ new
@@ -1,40 +1,16 @@
#!/usr/bin/env ruby
-require 'rubygems'
require 'net/ldap'
require 'optparse'
require 'yaml'
-require 'logger'
require 'kwalify'
+require 'pg'
+require "pg_ldap_sync/logger"
-begin
- require 'pg'
-rescue LoadError => e
- begin
- require 'postgres'
- class PGconn
- alias initialize_before_hash_change initialize
- def initialize(*args)
- arg = args.first
- if args.length==1 && arg.kind_of?(Hash)
- initialize_before_hash_change(arg[:host], arg[:port], nil, nil, arg[:dbname], arg[:user], arg[:password])
- else
- initialize_before_hash_change(*args)
- end
- end
- end
- rescue LoadError
- raise e
- end
-end
-
-require 'pg_ldap_sync'
-
module PgLdapSync
class Application
- class LdapError < RuntimeError; end
attr_accessor :config_fname
attr_accessor :log
attr_accessor :test
def string_to_symbol(hash)
@@ -56,11 +32,11 @@
errors = validator.validate(config)
if errors && !errors.empty?
errors.each do |err|
log.fatal "error in #{fname}: [#{err.path}] #{err.message}"
end
- exit(-1)
+ raise InvalidConfig, 78 # EX_CONFIG
end
end
def read_config_file(fname)
raise "Config file #{fname.inspect} does not exist" unless File.exist?(fname)
@@ -128,17 +104,21 @@
return groups
end
PgRole = Struct.new :name, :member_names
+ # List of default roles taken from https://www.postgresql.org/docs/current/static/default-roles.html
+ PG_BUILTIN_ROLES = %w[ pg_signal_backend pg_monitor pg_read_all_settings pg_read_all_stats pg_stat_scan_tables]
+
def search_pg_users
pg_users_conf = @config[:pg_users]
users = []
res = pg_exec "SELECT rolname FROM pg_roles WHERE #{pg_users_conf[:filter]}"
res.each do |tuple|
user = PgRole.new tuple[0]
+ next if PG_BUILTIN_ROLES.include?(user.name)
log.info{ "found pg-user: #{user.name.inspect}"}
users << user
end
return users
end
@@ -147,13 +127,14 @@
pg_groups_conf = @config[:pg_groups]
groups = []
res = pg_exec "SELECT rolname, oid FROM pg_roles WHERE #{pg_groups_conf[:filter]}"
res.each do |tuple|
- res2 = pg_exec "SELECT pr.rolname FROM pg_auth_members pam JOIN pg_roles pr ON pr.oid=pam.member WHERE pam.roleid=#{PGconn.escape(tuple[1])}"
+ res2 = pg_exec "SELECT pr.rolname FROM pg_auth_members pam JOIN pg_roles pr ON pr.oid=pam.member WHERE pam.roleid=#{@pgconn.escape_string(tuple[1])}"
member_names = res2.map{|row| row[0] }
group = PgRole.new tuple[0], member_names
+ next if PG_BUILTIN_ROLES.include?(group.name)
log.info{ "found pg-group: #{group.name.inspect} with members: #{member_names.inspect}"}
groups << group
end
return groups
end
@@ -209,14 +190,25 @@
"#{type} stat: create: #{roles.count{|r| r.state==:create }} drop: #{roles.count{|r| r.state==:drop }} keep: #{roles.count{|r| r.state==:keep }}"
}
return roles
end
+ def try_sql(text)
+ begin
+ @pgconn.exec "SAVEPOINT try_sql;"
+ @pgconn.exec text
+ rescue PG::Error => err
+ @pgconn.exec "ROLLBACK TO try_sql;"
+
+ log.error{ "#{err} (#{err.class})" }
+ end
+ end
+
def pg_exec_modify(sql)
log.info{ "SQL: #{sql}" }
unless self.test
- res = @pgconn.exec sql
+ try_sql sql
end
end
def pg_exec(sql)
res = @pgconn.exec sql
@@ -312,39 +304,49 @@
@ldap = Net::LDAP.new @config[:ldap_connection]
ldap_users = uniq_names search_ldap_users
ldap_groups = uniq_names search_ldap_groups
# gather PGs users and groups
- @pgconn = PGconn.connect @config[:pg_connection]
- pg_users = uniq_names search_pg_users
- pg_groups = uniq_names search_pg_groups
+ @pgconn = PG.connect @config[:pg_connection]
+ begin
+ @pgconn.transaction do
+ pg_users = uniq_names search_pg_users
+ pg_groups = uniq_names search_pg_groups
- # compare LDAP to PG users and groups
- mroles = match_roles(ldap_users, pg_users, :user)
- mroles += match_roles(ldap_groups, pg_groups, :group)
+ # compare LDAP to PG users and groups
+ mroles = match_roles(ldap_users, pg_users, :user)
+ mroles += match_roles(ldap_groups, pg_groups, :group)
- # compare LDAP to PG memberships
- mmemberships = match_memberships(ldap_users+ldap_groups, pg_users+pg_groups)
+ # compare LDAP to PG memberships
+ mmemberships = match_memberships(ldap_users+ldap_groups, pg_users+pg_groups)
- # drop/revoke roles/memberships first
- sync_membership_to_pg(mmemberships, :revoke)
- sync_roles_to_pg(mroles, :drop)
- # create/grant roles/memberships
- sync_roles_to_pg(mroles, :create)
- sync_membership_to_pg(mmemberships, :grant)
+ # drop/revoke roles/memberships first
+ sync_membership_to_pg(mmemberships, :revoke)
+ sync_roles_to_pg(mroles, :drop)
+ # create/grant roles/memberships
+ sync_roles_to_pg(mroles, :create)
+ sync_membership_to_pg(mmemberships, :grant)
+ end
+ ensure
+ @pgconn.close
+ end
- @pgconn.close
+ # Determine exitcode
+ if log.had_errors?
+ raise ErrorExit, 1
+ end
end
def self.run(argv)
s = self.new
s.config_fname = '/etc/pg_ldap_sync.yaml'
- s.log = Logger.new(STDOUT)
+ s.log = Logger.new($stdout, @error_counters)
s.log.level = Logger::ERROR
OptionParser.new do |opts|
+ opts.version = VERSION
opts.banner = "Usage: #{$0} [options]"
- opts.on("-v", "--[no-]verbose", "Increase verbose level"){ s.log.level-=1 }
+ opts.on("-v", "--[no-]verbose", "Increase verbose level"){|v| s.log.level += v ? -1 : 1 }
opts.on("-c", "--config FILE", "Config file [#{s.config_fname}]", &s.method(:config_fname=))
opts.on("-t", "--[no-]test", "Don't do any change in the database", &s.method(:test=))
opts.parse!(argv)
end