require 'test-unit' require 'erb' require 'yaml' require 'socket' require 'rbconfig' require 'tempfile' require 'active_ldap' require File.join(File.expand_path(File.dirname(__FILE__)), "command") LDAP_ENV = "test" unless defined?(LDAP_ENV) module AlTestUtils def self.included(base) base.class_eval do include ActiveLdap::GetTextSupport include Utilities include Config include Connection include Populate include TemporaryEntry include CommandSupport include MockLogger include Omittable end end module Utilities def dn(string) ActiveLdap::DN.parse(string) end end module Config def setup super @base_dir = File.expand_path(File.dirname(__FILE__)) @top_dir = File.expand_path(File.join(@base_dir, "..")) @example_dir = File.join(@top_dir, "examples") @fixtures_dir = File.join(@base_dir, "fixtures") current_config_file = File.expand_path("config.yaml") test_config_file = File.join(@base_dir, "config.yaml") if File.exist?(current_config_file) @config_file = current_config_file else @config_file = test_config_file end ActiveLdap::Base.configurations = read_config end def teardown super end def current_configuration ActiveLdap::Base.configurations[LDAP_ENV] end def read_config unless File.exist?(@config_file) raise "config file for testing doesn't exist: #{@config_file}" end erb = ERB.new(File.read(@config_file)) erb.filename = @config_file config = YAML.load(erb.result) _adapter = adapter config.each do |key, value| value["adapter"] = _adapter if _adapter end config end def adapter ENV["ACTIVE_LDAP_TEST_ADAPTER"] end def fixture(*components) File.join(@fixtures_dir, *components) end end module ExampleFile def certificate_path File.join(@example_dir, 'example.der') end @@certificate = nil def certificate return @@certificate if @@certificate if File.exist?(certificate_path) @@certificate = read_binary_file(certificate_path) return @@certificate end require 'openssl' rsa = OpenSSL::PKey::RSA.new(512) comment = "Generated by Ruby/OpenSSL" cert = OpenSSL::X509::Certificate.new cert.version = 3 cert.serial = 0 subject = [["OU", "test"], ["CN", Socket.gethostname]] name = OpenSSL::X509::Name.new(subject) cert.subject = name cert.issuer = name cert.not_before = Time.now cert.not_after = Time.now + (365*24*60*60) cert.public_key = rsa.public_key ef = OpenSSL::X509::ExtensionFactory.new(nil, cert) ef.issuer_certificate = cert cert.extensions = [ ef.create_extension("basicConstraints","CA:FALSE"), ef.create_extension("keyUsage", "keyEncipherment"), ef.create_extension("subjectKeyIdentifier", "hash"), ef.create_extension("extendedKeyUsage", "serverAuth"), ef.create_extension("nsComment", comment), ] aki = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") cert.add_extension(aki) cert.sign(rsa, OpenSSL::Digest::SHA1.new) @@certificate = cert.to_der @@certificate end def jpeg_photo_path File.join(@example_dir, 'example.jpg') end def jpeg_photo read_binary_file(jpeg_photo_path) end def read_binary_file(path) File.open(path, "rb") do |input| input.set_encoding("ascii-8bit") if input.respond_to?(:set_encoding) input.read end end end module Connection def setup super ActiveLdap::Base.setup_connection end def teardown ActiveLdap::Base.remove_active_connections! super end end module Populate def setup @dumped_data = nil super begin @dumped_data = ActiveLdap::Base.dump(:scope => :sub) rescue ActiveLdap::ConnectionError end ActiveLdap::Base.delete_all(nil, :scope => :sub) populate end def teardown if @dumped_data ActiveLdap::Base.setup_connection ActiveLdap::Base.delete_all(nil, :scope => :sub) ActiveLdap::Base.load(@dumped_data) end super end def populate populate_base populate_ou populate_user_class populate_group_class populate_group_of_urls_class populate_associations end def populate_base ActiveLdap::Populate.ensure_base end def ou_class(prefix="") ou_class = Class.new(ActiveLdap::Base) ou_class.ldap_mapping(:dn_attribute => "ou", :prefix => prefix, :classes => ["top", "organizationalUnit"]) ou_class end def dc_class(prefix="") dc_class = Class.new(ActiveLdap::Base) dc_class.ldap_mapping(:dn_attribute => "dc", :prefix => prefix, :classes => ["top", "dcObject", "organization"]) dc_class end def entry_class(prefix="") entry_class = Class.new(ActiveLdap::Base) entry_class.ldap_mapping(:prefix => prefix, :scope => :sub, :classes => ["top"]) entry_class.dn_attribute = nil entry_class end def populate_ou %w(Users Groups GroupOfURLsSet).each do |name| make_ou(name) end end def make_ou(name) ActiveLdap::Populate.ensure_ou(name) end def make_dc(name) ActiveLdap::Populate.ensure_dc(name) end def populate_user_class @user_class = Class.new(ActiveLdap::Base) @user_class_classes = ["posixAccount", "person"] @user_class.ldap_mapping :dn_attribute => "uid", :prefix => "ou=Users", :scope => :sub, :classes => @user_class_classes assign_class_name(@user_class, "User") end def populate_group_class @group_class = Class.new(ActiveLdap::Base) @group_class.ldap_mapping :prefix => "ou=Groups", :scope => :sub, :classes => ["posixGroup"] assign_class_name(@group_class, "Group") end def populate_group_of_urls_class @group_of_urls_class = Class.new(ActiveLdap::Base) @group_of_urls_class.ldap_mapping :prefix => "ou=GroupOfURLsSet", :scope => :sub, :classes => ["groupOfURLs"] assign_class_name(@group_of_urls_class, "GroupOfURLs") end def populate_associations @user_class.belongs_to :groups, :many => "memberUid" @user_class.belongs_to :primary_group, :foreign_key => "gidNumber", :primary_key => "gidNumber" @group_class.has_many :members, :wrap => "memberUid" @group_class.has_many :primary_members, :foreign_key => "gidNumber", :primary_key => "gidNumber" @user_class.set_associated_class(:groups, @group_class) @user_class.set_associated_class(:primary_group, @group_class) @group_class.set_associated_class(:members, @user_class) @group_class.set_associated_class(:primary_members, @user_class) end def assign_class_name(klass, name) singleton_class = class << klass; self; end singleton_class.send(:define_method, :name) do name end if Object.const_defined?(klass.name) Object.send(:remove_const, klass.name) end Object.const_set(klass.name, klass) end end module TemporaryEntry include ExampleFile def setup super @user_index = 0 @group_index = 0 @group_of_urls_index = 0 @temporary_uids = [] end def teardown @temporary_uids.each do |uid| delete_temporary_user(uid) end super end def delete_temporary_user(uid) return unless @user_class.exists?(uid) @user_class.search(:value => uid) do |dn, attribute| @user_class.remove_connection(dn) @user_class.delete(dn) end end def build_temporary_user(config={}) uid = config[:uid] || "temp-user#{@user_index}" password = config[:password] || "password#{@user_index}" uid_number = config[:uid_number] || default_uid gid_number = config[:gid_number] || default_gid home_directory = config[:home_directory] || "/nonexistent" see_also = config[:see_also] user = nil _wrap_assertion do assert(!@user_class.exists?(uid)) assert_raise(ActiveLdap::EntryNotFound) do @user_class.find(uid).dn end user = @user_class.new(uid) assert(user.new_entry?) user.cn = user.uid user.sn = user.uid user.uid_number = uid_number user.gid_number = gid_number user.home_directory = home_directory user.user_password = ActiveLdap::UserPassword.ssha(password) user.see_also = see_also unless config[:simple] user.add_class('shadowAccount', 'inetOrgPerson', 'organizationalPerson') user.user_certificate = certificate user.jpeg_photo = jpeg_photo end user.save assert(!user.new_entry?) end [@user_class.find(user.uid), password] end def make_temporary_user(config={}) @user_index += 1 config = config.merge(uid: config[:uid] || "temp-user#{@user_index}") uid = config[:uid] @temporary_uids << uid if block_given? ensure_delete_user(uid) do yield(*build_temporary_user(config)) end else build_temporary_user(config) end end def make_temporary_group(config={}) @group_index += 1 cn = config[:cn] || "temp-group#{@group_index}" ensure_delete_group(cn) do gid_number = config[:gid_number] || default_gid _wrap_assertion do assert(!@group_class.exists?(cn)) assert_raise(ActiveLdap::EntryNotFound) do @group_class.find(cn) end group = @group_class.new(cn) assert(group.new_entry?) group.gid_number = gid_number assert(group.save) assert(!group.new_entry?) yield(@group_class.find(group.cn)) end end end def make_temporary_group_of_urls(config={}) @group_of_urls_index += 1 cn = config[:cn] || "temp-group-of-urls-#{@group_of_urls_index}" ensure_delete_group_of_urls(cn) do _wrap_assertion do assert(!@group_of_urls_class.exists?(cn)) assert_raise(ActiveLdap::EntryNotFound) do @group_of_urls_class.find(cn) end group_of_urls = @group_of_urls_class.new(cn) assert(group_of_urls.new_entry?) group_of_urls.member_url = config[:member_url] assert(group_of_urls.save!) assert(!group_of_urls.new_entry?) yield(@group_of_urls_class.find(group_of_urls.cn)) end end end def ensure_delete_user(uid) yield(uid) ensure delete_temporary_user(uid) @temporary_uids.delete(uid) end def ensure_delete_group(cn) yield(cn) ensure @group_class.delete(cn) if @group_class.exists?(cn) end def ensure_delete_group_of_urls(cn) yield(cn) ensure @group_of_urls_class.delete(cn) if @group_of_urls_class.exists?(cn) end def default_uid "10000#{@user_index}" end def default_gid "10000#{@group_index}" end end module CommandSupport def setup super @fakeroot = "fakeroot" @ruby = File.join(::RbConfig::CONFIG["bindir"], ::RbConfig::CONFIG["RUBY_INSTALL_NAME"]) @top_dir = File.expand_path(File.join(File.dirname(__FILE__), "..")) @examples_dir = File.join(@top_dir, "examples") @lib_dir = File.join(@top_dir, "lib") @ruby_args = [ "-I", @examples_dir, "-I", @lib_dir, ] end def run_command(*args, &block) if RUBY_VERSION >= "2.7" omit("Need to fix an optional arguments warning in net-ldap: " + "ruby-ldap/ruby-net-ldap/pull/342") end file = Tempfile.new("al-command-support") file.open file.puts(ActiveLdap::Base.configurations["test"].to_yaml) file.close run_ruby(*[@command, "--config", file.path, *args], &block) end def run_ruby(*ruby_args, &block) args = [@ruby, *@ruby_args] args.concat(ruby_args) Command.run(*args, &block) end def run_ruby_with_fakeroot(*ruby_args, &block) args = [@fakeroot, @ruby, *@ruby_args] args.concat(ruby_args) Command.run(*args, &block) end end module MockLogger def make_mock_logger logger = Object.new class << logger def messages(type) @messages ||= {} @messages[type] ||= [] @messages[type] end def info(content=nil) messages(:info) << (block_given? ? yield : content) end def warn(content=nil) messages(:warn) << (block_given? ? yield : content) end def error(content=nil) messages(:error) << (block_given? ? yield : content) end end logger end def with_mock_logger original_logger = ActiveLdap::Base.logger mock_logger = make_mock_logger ActiveLdap::Base.logger = mock_logger yield(mock_logger) ensure ActiveLdap::Base.logger = original_logger end end module Omittable def omit_if_jruby(message=nil) return unless RUBY_PLATFORM == "java" omit(message || "This test is not for JRuby") end def omit_unless_jruby(message=nil) return if RUBY_PLATFORM == "java" omit(message || "This test is only for JRuby") end def omit_if_ldap(message=nil) return if current_configuration[:adapter] == "ldap" omit(message || "This test is not for ruby-ldap") end end end