Commits

Michael Granger committed 05c994a

Checkpoint commit; got testing server working

Comments (0)

Files changed (12)

+# -*- ruby -*-
+
+puts ">>> Adding 'lib' to load path..."
+$LOAD_PATH.unshift( "lib", "ext" )
+
+# Try to require the 'openldap' library
+begin
+	$stderr.puts "Loading OpenLDAP..."
+	require 'openldap'
+rescue => e
+	$stderr.puts "Ack! OpenLDAP failed to load: #{e.message}\n\t" +
+		e.backtrace.join( "\n\t" )
+end
+
+
 end
 
 # Build constants
-BASEDIR = Pathname( __FILE__ ).dirname.relative_path_from( Pathname.pwd )
-SPECDIR = BASEDIR + 'spec'
-LIBDIR  = BASEDIR + 'lib'
-EXTDIR  = BASEDIR + 'ext'
+BASEDIR      = Pathname( __FILE__ ).dirname.relative_path_from( Pathname.pwd )
+SPECDIR      = BASEDIR + 'spec'
+LIBDIR       = BASEDIR + 'lib'
+EXTDIR       = BASEDIR + 'ext'
 
-DLEXT   = RbConfig::CONFIG['DLEXT']
-EXT     = LIBDIR + "bluecloth_ext.#{DLEXT}"
-RUBY    = RbConfig.expand( "$(prefix)/$(ruby_install_name)" )
+TEST_WORKDIR = BASEDIR + 'test_workdir'
+CLOBBER.include( TEST_WORKDIR.to_s )
+
+DLEXT        = RbConfig::CONFIG['DLEXT']
+EXT          = LIBDIR + "openldap_ext.#{DLEXT}"
+RUBY         = RbConfig.expand( "$(prefix)/$(ruby_install_name)" )
 
 VALGRIND_OPTIONS = [
 	"--num-callers=50",
 
 # Load Hoe plugins
 Hoe.plugin :mercurial
-Hoe.plugin :yard
 Hoe.plugin :signing
 
 Hoe.plugins.delete :rubyforge
 
 	self.developer 'Michael Granger', 'ged@FaerieMUD.org'
 
-	self.dependency 'rake-compiler', '~> 0.7', :developer
+	self.dependency 'rake-compiler', '~> 0.8', :developer
 	self.dependency 'hoe-deveiate',  '~> 0.2', :developer
 
 	self.spec_extras[:licenses] = ["BSD"]

ext/openldap_ext/connection.c

  * -------------------------------------------------------------- */
 
 /*
- * call-seq:
- *    OpenLDAP::Connection.new( *uris )           -> conn
- *
- * Create a new OpenLDAP::Connection object using the given +uris+.
- *
+ * Backend method for OpenLDAP::Connection#initialize.
  */
 static VALUE
-ropenldap_conn_initialize( VALUE self, VALUE urls )
+ropenldap_conn__initialize( VALUE self, VALUE urls )
 {
 	ropenldap_log_obj( self, "debug", "Initializing 0x%x", self );
 
 		char *url = NULL;
 		struct ropenldap_connection *conn;
 		int result = 0;
-		int proto_ver = 3;
 
-		urlstring = rb_funcall( urls, rb_intern("join"), 1, rb_str_new(" ", 1) );
+		urlstring = rb_obj_as_string( urls );
 		url = RSTRING_PTR( rb_obj_as_string(urlstring) );
 
 		if ( !ldap_is_ldap_url(url) )
 
 /*
  * Start TLS synchronously; called from ropenldap_conn__start_tls after
- * the GIL is released.
+ * the GVL is released.
  */
-static VALUE
-ropenldap_conn__start_tls_body( void *ptr )
+static void *
+ropenldap_conn__start_tls_blocking( void *ptr )
 {
 	LDAP *ld = ptr;
-	return (VALUE)ldap_start_tls_s( ld, NULL, NULL );
+	int rval = ldap_start_tls_s( ld, NULL, NULL );
+	return (void *)(VALUE)rval;
 }
 
 
 /*
- * #_start_ls: backend of the #start_tls method.
+ * #_start_tls: backend of the #start_tls method.
  */
 static VALUE
 ropenldap_conn__start_tls( VALUE self )
 	int result;
 
 	ropenldap_log_obj( self, "debug", "Starting TLS..." );
-	result = (int)rb_thread_blocking_region( ropenldap_conn__start_tls_body, (void *)ptr->ldap,
-		RUBY_UBF_IO, NULL );
+	result = (int)(VALUE)rb_thread_call_without_gvl( ropenldap_conn__start_tls_blocking,
+	                                                 (void *)ptr->ldap, RUBY_UBF_IO, NULL );
 	ropenldap_check_result( result, "ldap_start_tls_s" );
 	ropenldap_log_obj( self, "debug", "  TLS started." );
 
 	struct ropenldap_connection *ptr = ropenldap_get_conn( self );
 	const int optval = NUM2INT( opt );
 
+	ropenldap_log_obj( self, "debug", "Setting LDAP_OPT_X_TLS_REQUIRE_CERT to %d", optval );
+
 	if ( ldap_set_option(ptr->ldap, LDAP_OPT_X_TLS_REQUIRE_CERT, &optval) != LDAP_OPT_SUCCESS )
 		rb_raise( ropenldap_eOpenLDAPError, "couldn't set option: LDAP_OPT_X_TLS_REQUIRE_CERT" );
 
 	/* OpenLDAP::Connection */
 	ropenldap_cOpenLDAPConnection =
 		rb_define_class_under( ropenldap_mOpenLDAP, "Connection", rb_cObject );
-	rb_include_module( ropenldap_cOpenLDAPConnection, ropenldap_mOpenLDAPLoggable );
 
 	rb_define_alloc_func( ropenldap_cOpenLDAPConnection, ropenldap_conn_s_allocate );
 
 	rb_define_protected_method( ropenldap_cOpenLDAPConnection, "_initialize",
-	                            ropenldap_conn_initialize, -2 );
+	                            ropenldap_conn__initialize, 1 );
 
 	rb_define_method( ropenldap_cOpenLDAPConnection, "uris", ropenldap_conn_uris, 0 );
 	rb_define_method( ropenldap_cOpenLDAPConnection, "fdno", ropenldap_conn_fdno, 0 );

ext/openldap_ext/extconf.rb

 end
 
 
-dir_config( 'openldap' )
+# if dirs = dir_config( 'openldap' )
+# 	$stderr.puts "Adding rpath pointing to #{dirs.last}lib"
+# 	$LDFLAGS << " -Wl,-rpath #{dirs.last}lib"
+# end
+
+have_func 'rb_thread_call_without_gvl' or abort "no rb_thread_call_without_gvl()"
+have_func 'rb_thread_call_with_gvl' or abort "no rb_thread_call_with_gvl()"
 
 find_header( 'ldap.h' ) or
 	abort( "missing ldap.h; do you need to install a developer package?" )

ext/openldap_ext/openldap.c

 #include "openldap.h"
 
 VALUE ropenldap_mOpenLDAP;
-VALUE ropenldap_mOpenLDAPLoggable;
 
 VALUE ropenldap_eOpenLDAPError;
 
 	ropenldap_mOpenLDAP = rb_define_module( "OpenLDAP" );
 
 	rb_require( "openldap/mixins" );
-	ropenldap_mOpenLDAPLoggable = rb_define_module_under( ropenldap_mOpenLDAP, "Loggable" );
+
 	ropenldap_eOpenLDAPError =
 		rb_define_class_under( ropenldap_mOpenLDAP, "Error", rb_eRuntimeError );
 

ext/openldap_ext/openldap.h

 #include <ldap.h>
 
 #include <ruby.h>
+#include <ruby/thread.h>
 
 #include "extconf.h"
 
 extern VALUE ropenldap_rbmURI;
 
 extern VALUE ropenldap_mOpenLDAP;
-extern VALUE ropenldap_mOpenLDAPLoggable;
 
 extern VALUE ropenldap_cOpenLDAPConnection;
 
 	# Load the remaining Ruby parts of the library
 	require 'openldap/exceptions'
 
+
+	### Shortcut connection method: return a OpenLDAP::Connection object that will use
+	### the specified +urls+ (or )
+	def self::connect( *urls )
+		
+	end
+
 end # module OpenLDAP
 
 

lib/openldap/connection.rb

 		options = DEFAULT_OPTIONS.merge( options )
 
 		url_strings = urls.map( &self.method(:simplify_url) )
-		self._initialize( url_strings )
+		self._initialize( url_strings.join(' ') )
 
 		# Set options
 		options.each do |opt, val|
 	### authentication mechanism with a TLS session. As such, a non-default
 	### setting must be chosen to enable SASL EXTERNAL authentication.
 	def tls_require_cert=( strategy )
+		self.log.debug "Setting require_cert to strategy: %p" % [ strategy ]
 		numeric_opt = TLS_REQUIRE_CERT_STRATEGIES[ strategy ] or
 			raise IndexError, "unknown TLS certificate-checking strategy %p" % [strategy]
 		self._tls_require_cert=( numeric_opt )
 	end
 
 
+	### Return a String representation of the object suitable for debugging.
+	def inspect
+		return "#<%p:%#016x %s>"
+	end
+
+
 	#######
 	private
 	#######
 	def simplify_url( url )
 		url = URI( url ) unless url.is_a?( URI )
 		simpleurl = URI::Generic.build( :scheme => url.scheme, :host => url.host, :port => url.port )
-		self.log.info "Simplified URL %s to: %s" % [ url, simpleurl ]
+		self.log.info "Simplified URL %s to: %s" % [ url, simpleurl ] if url.to_s != simpleurl.to_s
 
 		return simpleurl.to_s
 	end

spec/data/testdata.ldif

+dn: dc=example,dc=com
+objectclass: dcObject
+objectclass: organization
+o: Example Organization
+dc: example
+
+dn: cn=admin,dc=example,dc=com
+objectclass: organizationalRole
+cn: admin
+

spec/lib/constants.rb

 
 	unless defined?( TEST_LDAP_URI )
 
-		TEST_LDAP_STRING = 'ldap://localhost:6363'
-		TEST_LDAP_URI = URI( TEST_LDAP_STRING )
+		TEST_LDAP_STRING     = 'ldap://localhost:6363'
+		TEST_LDAP_URI        = URI( TEST_LDAP_STRING )
 
-		TEST_LDAPS_STRING = 'ldaps://localhost:6363'
-		TEST_LDAPS_URI = URI( TEST_LDAPS_STRING )
+		TEST_LDAPS_STRING    = 'ldaps://localhost:6364'
+		TEST_LDAPS_URI       = URI( TEST_LDAPS_STRING )
 
 		TEST_LDAPBASE_STRING = "#{TEST_LDAP_STRING}/dc=example,dc=com"
-		TEST_LDAPBASE_URI = URI( TEST_LDAPBASE_STRING )
+		TEST_LDAPBASE_URI    = URI( TEST_LDAPBASE_STRING )
+
+		TEST_ADMIN_ROOT_DN   = "cn=admin,dc=example,dc=com"
+		TEST_ADMIN_PASSWORD  = 'secret'
 
 		constants.each do |cname|
 			const_get(cname).freeze

spec/lib/helpers.rb

 #!/usr/bin/ruby
 # coding: utf-8
 
+require 'erb'
 require 'pathname'
 require 'shellwords'
 require 'yaml'
 require 'openldap'
 require 'openldap/mixins'
 
-require 'spec/lib/constants'
+require_relative 'constants'
 
 
 ### RSpec helper functions.
 
 	BASEDIR        = Pathname( __FILE__ ).dirname.parent.parent
 
-	TESTING_SLAPD_URI = 'ldap://localhost:6363'
-	TESTING_SLAPD_SSL_URI = 'ldaps://localhost:6364'
+	TESTING_SLAPD_URI = 'ldap://localhost:6363/dc=example,dc=com'
+	TESTING_SLAPD_SSL_URI = 'ldaps://localhost:6364/dc=example,dc=com'
 
 	TEST_WORKDIR   = BASEDIR + 'test_workdir'
 	TEST_DATADIR   = TEST_WORKDIR + 'data'
 	### Start a localized slapd daemon for testing.
 	def start_testing_slapd
 
-		self.create_test_directories
-		self.copy_test_files
-		self.generate_ssl_cert
-		self.install_initial_data
-		slapd_pid = self.start_slapd
+		unless TEST_DATADIR.exist?
+			self.create_test_directories
+			self.copy_test_files
+			self.generate_ssl_cert
+			self.install_initial_data
+		end
 
-		return slapd_pid
+		return self.start_slapd
 	end
 
 
 				$stderr.puts "  killed."
 			end
 		end
-
-		unless $DEBUG || ENV['MAINTAINER_MODE']
-			$stderr.puts "Cleaning up #{TEST_WORKDIR}..."
-			TEST_WORKDIR.rmtree
-		end
 	end
 
 
 
 	### Copy over any files necessary for testing to the testing directory.
 	def copy_test_files
-		install SPEC_SLAPDCONF, TEST_WORKDIR
-		install SPEC_DBCONFIG, TEST_DATADIR
+		install_with_filter( SPEC_SLAPDCONF, TEST_WORKDIR, binding() )
+		install_with_filter( SPEC_DBCONFIG, TEST_DATADIR, binding )
+	end
+
+
+	### Copy the file from +source+ to +dest+ with ERB filtering using the given
+	### +filter_binding+.
+	def install_with_filter( source, dest, filter_binding )
+		dest = dest + source.basename if dest.directory?
+		contents = ERB.new( source.read(:encoding => 'UTF-8') )
+		dest.open( 'w' ) do |io|
+			io.print( contents.result(filter_binding) )
+		end
 	end
 
 
 	### Generate a self-signed cert for testing SSL/TlS connections
 	### Mostly stolen from https://gist.github.com/nickyp/886884
 	def generate_ssl_cert
-		key = OpenSSL::PKey::RSA.new( 1024 )
-		public_key = key.public_key
+		request_key  = TEST_WORKDIR + 'example.key.org'
+		cert_request = TEST_WORKDIR + 'example.csr'
+		signing_key  = TEST_WORKDIR + 'example.key'
+		cert         = TEST_WORKDIR + 'example.crt'
 
-		subject = "/CN=*.example.com"
+		unless File.exist?( cert )
+			system 'openssl', 'req',
+				'-new', '-newkey', 'rsa:4096',
+				'-days', '365', '-nodes', '-x509',
+				'-subj', '/C=US/ST=Oregon/L=Portland/O=IT/CN=localhost',
+				'-keyout', signing_key.to_s,
+				'-out', cert.to_s
 
-		cert = OpenSSL::X509::Certificate.new
-		cert.subject = cert.issuer = OpenSSL::X509::Name.parse( subject )
-		cert.not_before = Time.now
-		cert.not_after = Time.now + 365 * 24 * 60 * 60
-		cert.public_key = public_key
-		cert.serial = 0x0
-		cert.version = 2
+			system 'openssl', 'req',
+				'-new',
+				'-subj', '/C=US/ST=Oregon/L=Portland/O=IT/CN=localhost',
+				'-key', request_key.to_s,
+				'-out', cert_request.to_s
 
-		ef = OpenSSL::X509::ExtensionFactory.new
-		ef.subject_certificate = cert
-		ef.issuer_certificate = cert
-		cert.extensions = [
-			ef.create_extension( "basicConstraints", "CA:TRUE" , true ),
-			ef.create_extension( "subjectKeyIdentifier", "hash" ),
-			# ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
-		]
-		ext = ef.create_extension( "authorityKeyIdentifier", "keyid:always,issuer:always" )
-		cert.add_extension( ext )
+			system 'openssl', 'rsa',
+				'-in', request_key.to_s,
+				'-out', signing_key.to_s
 
-		cert.sign( key, OpenSSL::Digest::SHA1.new )
-
-		pemfile = TEST_WORKDIR + 'example.pem'
-		pemfile.open( 'w' ) {|io|
-			io.puts(cert.to_pem)
-			io.puts(key.to_pem)
-		}
+			system 'openssl', 'x509',
+				'-req', '-days', '365',
+				'-in', cert_request.to_s,
+				'-signkey', signing_key.to_s,
+				'-out', cert.to_s
+		end
 	end
 
 
 	### Install the initial testing data into the data dir in +TEST_WORKDIR+.
 	def install_initial_data
+		$stderr.print "Installing testing directory data..."
 		slapadd = self.find_binary( 'slapadd' )
 		ldiffile = SPEC_LDIF.to_s
 		configfile = TEST_WORKDIR + 'slapd.conf'
 			'-l', ldiffile
 		]
 
+		$stderr.puts( ">>> ", Shellwords.join(cmd) )
 		system( *cmd, chdir: TEST_WORKDIR.to_s ) or
-		raise "Couldn't load initial data: #{Shellwords.join(cmd)}"
+			raise "Couldn't load initial data: #{Shellwords.join(cmd)}"
+		$stderr.puts "installed."
 	end
 
 
 			'-h', "ldap://localhost:6363 ldaps://localhost:6364"
 		]
 
-		puts( Shellwords.join(cmd) )
+		$stderr.puts( ">>> ", Shellwords.join(cmd) )
 		pid = spawn( *cmd, chdir: TEST_WORKDIR.to_s, [:out,:err] => logio )
 
 		$stderr.puts "started at PID %d" % [ pid ]

spec/openldap/connection_spec.rb

 #!/usr/bin/env rspec -cfd -b
 
-BEGIN {
-	require 'pathname'
-	basedir = Pathname( __FILE__ ).dirname.parent.parent
-	libdir = basedir + 'lib'
+require_relative '../lib/helpers'
 
-	$LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
-	$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
-}
-
+require 'pry'
 require 'uri'
 require 'rspec'
-require 'spec/lib/helpers'
 require 'openldap/connection'
 
 describe OpenLDAP::Connection do
 
 	before( :all ) do
-		setup_logging( :debug )
+		setup_logging()
 		@slapd_pid = start_testing_slapd()
 	end
 
 		conn.uris.should == [ TEST_LDAP_URI, TEST_LDAPS_URI ]
 	end
 
-	context "an instance" do
+	context "instances" do
 
 		before( :each ) do
 			@conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
 			@conn.async_connect?.should be_false()
 		end
 
-	end
 
-	context "an unconnected instance" do
-
-		before( :each ) do
-			@conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
-		end
-
-		it "knows that its file-descriptor is nil" do
+		it "know that their file-descriptors are nil" do
 			@conn.fdno.should be_nil()
 		end
 
-		it "fails to start TLS with strict cert-checking enabled", :with_ldap_server => true do;
+
+		it "fail to start TLS with strict cert-checking enabled" do
 			expect {
 				@conn.start_tls( :tls_require_cert => :demand )
-			}.to raise_error( OpenLDAP::ConnectError, /ldap_start_tls_s/i )
+			}.to raise_error( OpenLDAP::ServerDown, /ldap_start_tls_s/i )
 		end
 
-		it "can start TLS negotiation synchronously", :with_ldap_server => true do;
-			@conn.start_tls( :tls_require_cert => :never )
+
+		it "can start TLS negotiation synchronously" do
+			# pending "Figuring out why this doesn't work" do
+				@conn.tls_cacertfile = 'test_workdir/example.pem'
+				@conn.start_tls( :tls_require_cert => :never )
+			# end
 		end
 
-	end
 
+		context "that are connected" do
 
-	context "a connected instance", :if => @slapd_pid do
+			before( :each ) do
+				@conn.start_tls( :tls_require_cert => :never )
+			end
 
-		before( :each ) do
-			@conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
-			@conn.start_tls
-		end
+			it "know what their file-descriptor number is" do
+				@conn.fdno.should be_a( Fixnum )
+				@conn.fdno.should > 2 # Something past STDERR
+			end
 
-		it "knows what its file-descriptor is" do
-			@conn.fdno.should be_a( Fixnum )
-			@conn.fdno.should > 2 # Something past STDERR
-		end
+			it "can return an IO for selecting against their file-descriptor" do
+				@conn.socket.should be_an( IO )
+				@conn.socket.fileno.should == @conn.fdno
+				@conn.socket.should_not be_autoclose()
+				@conn.socket.should_not be_close_on_exec()
+				@conn.socket.should be_binmode()
+			end
 
-		it "can return an IO for selecting against its file-descriptor" do
-			@conn.socket.should be_an( IO )
-			@conn.socket.fileno.should == @conn.fdno
-			@conn.socket.should_not be_autoclose()
-			@conn.socket.should_not be_close_on_exec()
-			@conn.socket.should be_binmode()
+			it "can bind as a user" do
+				@conn.bind( TEST_ADMIN_ROOT_DN, TEST_ADMIN_PASSWORD ).should == true
+			end
+
+
+
 		end
 
 	end