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