257b1a5e49
Run sync within a SQL transaction, so that no partial sync happens Don't abort on SQL errors, but print ERROR notice Exit with code 1 when any ERRORs were logged Silence the test suite, so that test runs are more clearly Use PG queries instead of psql in test. This is more flexible than parsing text outputs per Regexp.
195 lines
5.3 KiB
Ruby
195 lines
5.3 KiB
Ruby
require "minitest/autorun"
|
|
require 'minitest/hooks/test'
|
|
require "pg_ldap_sync"
|
|
require 'yaml'
|
|
require 'fileutils'
|
|
require_relative 'ldap_server'
|
|
|
|
class TestPgLdapSync < Minitest::Test
|
|
include Minitest::Hooks
|
|
|
|
@@logid = 0
|
|
|
|
def log_and_run( *cmd )
|
|
if $DEBUG
|
|
puts cmd.join(' ')
|
|
system( *cmd )
|
|
raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
|
|
else
|
|
fname = "temp/run_#{@@logid+=1}.log"
|
|
pid = Process.spawn( *cmd, [:out, :err] => [fname, "w"] )
|
|
Process.wait(pid)
|
|
unless $?.success?
|
|
$stderr.puts "Command failed: [%s]\n%s" % [cmd.join(' '), File.read(fname)]
|
|
end
|
|
File.unlink fname
|
|
raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
|
|
end
|
|
end
|
|
|
|
def start_ldap_server
|
|
@directory = [{}]
|
|
|
|
# Listen for incoming LDAP connections. For each one, create a Connection
|
|
# object, which will invoke a HashOperation object for each request.
|
|
|
|
@ldap_server = LDAP::Server.new(
|
|
:port => 1389,
|
|
:nodelay => true,
|
|
:listen => 10,
|
|
# :ssl_key_file => "key.pem",
|
|
# :ssl_cert_file => "cert.pem",
|
|
# :ssl_on_connect => true,
|
|
:operation_class => HashOperation,
|
|
:operation_args => @directory
|
|
)
|
|
@ldap_server.run_tcpserver
|
|
end
|
|
|
|
def stop_ldap_server
|
|
@ldap_server.stop
|
|
end
|
|
|
|
def start_pg_server
|
|
@port = 54321
|
|
ENV['PGPORT'] = @port.to_s
|
|
ENV['PGHOST'] = 'localhost'
|
|
unless File.exist?('temp/pg_data/PG_VERSION')
|
|
FileUtils.mkdir_p 'temp/pg_data'
|
|
log_and_run 'initdb', '-D', 'temp/pg_data', '--no-locale'
|
|
end
|
|
log_and_run 'pg_ctl', '-w', '-o', "-k.", '-D', 'temp/pg_data', 'start'
|
|
|
|
@pgconn = PG.connect dbname: 'postgres'
|
|
@pgconn.exec "SET client_min_messages to warning"
|
|
end
|
|
|
|
def stop_pg_server
|
|
@pgconn.close if @pgconn
|
|
log_and_run 'pg_ctl', '-w', '-o', "-k.", '-D', 'temp/pg_data', 'stop'
|
|
end
|
|
|
|
def before_all
|
|
super
|
|
ENV['LC_MESSAGES'] = 'C'
|
|
start_ldap_server
|
|
start_pg_server
|
|
end
|
|
|
|
def after_all
|
|
super
|
|
stop_ldap_server
|
|
stop_pg_server
|
|
end
|
|
|
|
def setup
|
|
@pgconn.exec "DROP ROLE IF EXISTS fred, wilma, \"Flintstones\", \"Wilmas\", \"All Users\", double_user"
|
|
end
|
|
|
|
def assert_role(role_name, attrs, member_of=[])
|
|
res = @pgconn.exec("SELECT * FROM pg_roles WHERE rolname = '#{@pgconn.escape_string(role_name)}'")
|
|
assert_equal 1, res.ntuples, "Role #{role_name} not found"
|
|
|
|
res2 = @pgconn.exec "SELECT pr.rolname FROM pg_auth_members pam JOIN pg_roles pr ON pr.oid=pam.roleid WHERE pam.member=#{res.to_a[0]['oid']}"
|
|
rolnames = res2.map{|t| t['rolname'] }
|
|
assert_equal member_of.sort, rolnames.sort
|
|
|
|
exp_attrs = []
|
|
exp_attrs << 'Cannot login' if res[0]['rolcanlogin'] != 't'
|
|
exp_attrs << 'Superuser' if res[0]['rolsuper'] == 't'
|
|
|
|
assert_equal attrs, exp_attrs.join(", ")
|
|
end
|
|
|
|
def refute_role(role_name)
|
|
res = @pgconn.exec("SELECT oid FROM pg_roles WHERE rolname = '#{@pgconn.escape_string(role_name)}'")
|
|
assert_equal 0, res.ntuples, "Role #{role_name} not found"
|
|
end
|
|
|
|
def exec_psql_du
|
|
text = if RUBY_PLATFORM=~/mingw|mswin/
|
|
`psql -c \\du postgres`
|
|
else
|
|
`psql -c \\\\du postgres`
|
|
end
|
|
puts text
|
|
return text
|
|
end
|
|
|
|
def load_ldap_fixture(fname)
|
|
yaml_fname = File.join(File.dirname(__FILE__), "fixtures/#{fname}.yaml")
|
|
@directory[0] = File.open(yaml_fname){|f| YAML::load(f.read) }
|
|
end
|
|
|
|
def sync_with_config(config="config-ldapdb")
|
|
PgLdapSync::Application.run(["-c", "test/fixtures/#{config}.yaml"] + ($DEBUG ? ["-vv"] : ["--no-verbose"]))
|
|
end
|
|
|
|
def sync_to_fixture(fixture: "ldapdb", config: "config-ldapdb")
|
|
load_ldap_fixture(fixture)
|
|
sync_with_config(config)
|
|
end
|
|
|
|
def sync_change
|
|
sync_to_fixture
|
|
|
|
yield(@directory)
|
|
|
|
sync_with_config
|
|
exec_psql_du if $DEBUG
|
|
end
|
|
|
|
def test_invalid_config
|
|
assert_output(/key 'ldap_users:' is required/) do
|
|
assert_raises(PgLdapSync::InvalidConfig) do
|
|
sync_with_config("config-invalid")
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_base_users_groups_memberships
|
|
sync_change{}
|
|
|
|
assert_role('All Users', 'Cannot login')
|
|
assert_role('Flintstones', 'Cannot login')
|
|
assert_role('Wilmas', 'Cannot login', ['All Users'])
|
|
assert_role('fred', '', ['All Users', 'Flintstones'])
|
|
assert_role('wilma', '', ['Flintstones', 'Wilmas'])
|
|
end
|
|
|
|
def test_add_membership
|
|
sync_change do |dir|
|
|
# add 'Fred' to 'Wilmas'
|
|
@directory[0]['cn=Wilmas,dc=example,dc=com']['member'] << 'cn=Fred Flintstone,dc=example,dc=com'
|
|
end
|
|
assert_role('fred', '', ['All Users', 'Flintstones', 'Wilmas'])
|
|
end
|
|
|
|
def test_revoke_membership
|
|
sync_change do |dir|
|
|
# revoke membership of 'wilma' to 'Flintstones'
|
|
dir[0]['cn=Flintstones,dc=example,dc=com']['member'].pop
|
|
end
|
|
assert_role('wilma', '', ['Wilmas'])
|
|
end
|
|
|
|
def test_rename_role
|
|
sync_change do |dir|
|
|
# rename role 'wilma'
|
|
dir[0]['cn=Wilma Flintstone,dc=example,dc=com']['sAMAccountName'] = ['Wilma Flintstone']
|
|
end
|
|
refute_role('wilma')
|
|
assert_role('Wilma Flintstone', '', ['Flintstones', 'Wilmas'])
|
|
end
|
|
|
|
def test_dont_stop_on_error
|
|
log_and_run 'psql', '-e', '-c', "CREATE ROLE double_user LOGIN", 'postgres'
|
|
|
|
assert_raises(PgLdapSync::ErrorExit) do
|
|
sync_change do |dir|
|
|
dir[0]['cn=double_user,dc=example,dc=com'] = {'sAMAccountName' => 'double_user'}
|
|
end
|
|
end
|
|
end
|
|
end
|