openldap 0.0.1pre11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rbconfig'
4
+ require 'erb'
5
+ require 'etc'
6
+ require 'logger'
7
+
8
+ require 'openldap'
9
+
10
+
11
+ #--
12
+ # A collection of mixins shared between OpenLDAP classes. Stolen mostly from ThingFish.
13
+ #
14
+ module OpenLDAP # :nodoc:
15
+
16
+ # Add logging to a OpenLDAP class. Including classes get #log and #log_debug methods.
17
+ module Loggable
18
+
19
+ LEVEL = {
20
+ :debug => Logger::DEBUG,
21
+ :info => Logger::INFO,
22
+ :warn => Logger::WARN,
23
+ :error => Logger::ERROR,
24
+ :fatal => Logger::FATAL,
25
+ }
26
+
27
+ ### A logging proxy class that wraps calls to the logger into calls that include
28
+ ### the name of the calling class.
29
+ class ClassNameProxy # :nodoc:
30
+
31
+ ### Create a new proxy for the given +klass+.
32
+ def initialize( klass, force_debug=false )
33
+ @classname = klass.name
34
+ @force_debug = force_debug
35
+ end
36
+
37
+ ### Delegate calls the global logger with the class name as the 'progname'
38
+ ### argument.
39
+ def method_missing( sym, msg=nil, &block )
40
+ return super unless LEVEL.key?( sym )
41
+ sym = :debug if @force_debug
42
+ OpenLDAP.logger.add( LEVEL[sym], msg, @classname, &block )
43
+ end
44
+ end # ClassNameProxy
45
+
46
+ #########
47
+ protected
48
+ #########
49
+
50
+ ### Return the proxied logger.
51
+ def log
52
+ @log_proxy ||= ClassNameProxy.new( self.class )
53
+ end
54
+
55
+ ### Return a proxied "debug" logger that ignores other level specification.
56
+ def log_debug
57
+ @log_debug_proxy ||= ClassNameProxy.new( self.class, true )
58
+ end
59
+ end # module Loggable
60
+
61
+
62
+ end # module OpenLDAP
63
+
64
+ # vim: set nosta noet ts=4 sw=4:
65
+
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'erb'
5
+
6
+ require 'openldap' unless defined?( OpenLDAP )
7
+
8
+ module OpenLDAP # :nodoc:
9
+
10
+ # A alternate formatter for Logger instances.
11
+ class LogFormatter < Logger::Formatter # :nodoc:
12
+
13
+ # The format to output unless debugging is turned on
14
+ DEFAULT_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s -- %7$s\n"
15
+
16
+ # The format to output if debugging is turned on
17
+ DEFAULT_DEBUG_FORMAT = "[%1$s.%2$06d %3$d/%4$s] %5$5s {%6$s} -- %7$s\n"
18
+
19
+
20
+ ### Initialize the formatter with a reference to the logger so it can check for log level.
21
+ def initialize( logger, format=DEFAULT_FORMAT, debug=DEFAULT_DEBUG_FORMAT ) # :notnew:
22
+ @logger = logger
23
+ @format = format
24
+ @debug_format = debug
25
+
26
+ super()
27
+ end
28
+
29
+ ######
30
+ public
31
+ ######
32
+
33
+ # The Logger object associated with the formatter
34
+ attr_accessor :logger
35
+
36
+ # The logging format string
37
+ attr_accessor :format
38
+
39
+ # The logging format string that's used when outputting in debug mode
40
+ attr_accessor :debug_format
41
+
42
+
43
+ ### Log using either the DEBUG_FORMAT if the associated logger is at ::DEBUG level or
44
+ ### using FORMAT if it's anything less verbose.
45
+ def call( severity, time, progname, msg )
46
+ args = [
47
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
48
+ time.usec, # %2$d
49
+ Process.pid, # %3$d
50
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
51
+ severity, # %5$s
52
+ progname, # %6$s
53
+ msg # %7$s
54
+ ]
55
+
56
+ if @logger.level == Logger::DEBUG
57
+ return self.debug_format % args
58
+ else
59
+ return self.format % args
60
+ end
61
+ end
62
+ end # class LogFormatter
63
+
64
+
65
+ # An alternate formatter for Logger instances that outputs +dd+ HTML
66
+ # fragments.
67
+ class HtmlLogFormatter < Logger::Formatter # :nodoc:
68
+ include ERB::Util # for html_escape()
69
+
70
+ # The default HTML fragment that'll be used as the template for each log message.
71
+ HTML_LOG_FORMAT = %q{
72
+ <div class="log-message %5$s">
73
+ <span class="log-time">%1$s.%2$06d</span>
74
+ [
75
+ <span class="log-pid">%3$d</span>
76
+ /
77
+ <span class="log-tid">%4$s</span>
78
+ ]
79
+ <span class="log-level">%5$s</span>
80
+ :
81
+ <span class="log-name">%6$s</span>
82
+ <span class="log-message-text">%7$s</span>
83
+ </div>
84
+ }
85
+
86
+ ### Override the logging formats with ones that generate HTML fragments
87
+ def initialize( logger, format=HTML_LOG_FORMAT ) # :notnew:
88
+ @logger = logger
89
+ @format = format
90
+ super()
91
+ end
92
+
93
+
94
+ ######
95
+ public
96
+ ######
97
+
98
+ # The HTML fragment that will be used as a format() string for the log
99
+ attr_accessor :format
100
+
101
+
102
+ ### Return a log message composed out of the arguments formatted using the
103
+ ### formatter's format string
104
+ def call( severity, time, progname, msg )
105
+ args = [
106
+ time.strftime( '%Y-%m-%d %H:%M:%S' ), # %1$s
107
+ time.usec, # %2$d
108
+ Process.pid, # %3$d
109
+ Thread.current == Thread.main ? 'main' : Thread.object_id, # %4$s
110
+ severity.downcase, # %5$s
111
+ progname, # %6$s
112
+ html_escape( msg ).gsub(/\n/, '<br />') # %7$s
113
+ ]
114
+
115
+ return self.format % args
116
+ end
117
+
118
+ end # class HtmlLogFormatter
119
+
120
+ end # module OpenLDAP
121
+
122
+ # vim: set nosta noet ts=4 sw=4:
123
+
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ require 'openldap'
6
+
7
+
8
+ module OpenLDAP::TestConstants
9
+
10
+ unless defined?( TEST_LDAP_URI )
11
+
12
+ TEST_CONFIG_FILE = Pathname( __FILE__ ).dirname.parent.parent + 'test.conf'
13
+
14
+ TEST_LOCAL_LDAP_STRING = 'ldap://localhost'
15
+ TEST_LOCAL_LDAP_URI = URI( TEST_LOCAL_LDAP_STRING )
16
+
17
+ TEST_LDAP_STRING = 'ldap://ldap.example.com'
18
+ TEST_LDAP_URI = URI( TEST_LDAP_STRING )
19
+
20
+ TEST_LDAPBASE_STRING = 'ldap://ldap.example.com/dc=example,dc=com'
21
+ TEST_LDAPBASE_URI = URI( TEST_LDAPBASE_STRING )
22
+
23
+ constants.each do |cname|
24
+ const_get(cname).freeze
25
+ end
26
+ end
27
+
28
+ end
29
+
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ BEGIN {
5
+ require 'rbconfig'
6
+ require 'pathname'
7
+ basedir = Pathname.new( __FILE__ ).dirname.parent
8
+
9
+ libdir = basedir + "lib"
10
+ extdir = libdir + Config::CONFIG['sitearch']
11
+
12
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
13
+ $LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
14
+ }
15
+
16
+ require 'pp'
17
+ require 'yaml'
18
+ require 'logger'
19
+
20
+ require 'openldap'
21
+ require 'openldap/mixins'
22
+
23
+ require 'spec/lib/constants'
24
+
25
+ ### Return a string-comparable version vector from +version+.
26
+ def vvec( version )
27
+ return version.split( '.' ).map( &:to_i ).pack( 'N' )
28
+ end
29
+
30
+
31
+ ### RSpec helper functions.
32
+ module OpenLDAP::SpecHelpers
33
+ include OpenLDAP::TestConstants
34
+
35
+ class ArrayLogger
36
+ ### Create a new ArrayLogger that will append content to +array+.
37
+ def initialize( array )
38
+ @array = array
39
+ end
40
+
41
+ ### Write the specified +message+ to the array.
42
+ def write( message )
43
+ @array << message
44
+ end
45
+
46
+ ### No-op -- this is here just so Logger doesn't complain
47
+ def close; end
48
+
49
+ end # class ArrayLogger
50
+
51
+
52
+ unless defined?( LEVEL )
53
+ LEVEL = {
54
+ :debug => Logger::DEBUG,
55
+ :info => Logger::INFO,
56
+ :warn => Logger::WARN,
57
+ :error => Logger::ERROR,
58
+ :fatal => Logger::FATAL,
59
+ }
60
+ end
61
+
62
+
63
+ ###############
64
+ module_function
65
+ ###############
66
+
67
+ ### Reset the logging subsystem to its default state.
68
+ def reset_logging
69
+ OpenLDAP.reset_logger
70
+ end
71
+
72
+
73
+ ### Alter the output of the default log formatter to be pretty in SpecMate output
74
+ def setup_logging( level=Logger::FATAL )
75
+
76
+ # Turn symbol-style level config into Logger's expected Fixnum level
77
+ if OpenLDAP::Loggable::LEVEL.key?( level )
78
+ level = OpenLDAP::Loggable::LEVEL[ level ]
79
+ end
80
+
81
+ logger = Logger.new( $stderr )
82
+ OpenLDAP.logger = logger
83
+ OpenLDAP.logger.level = level
84
+
85
+ # Only do this when executing from a spec in TextMate
86
+ if ENV['HTML_LOGGING'] || (ENV['TM_FILENAME'] && ENV['TM_FILENAME'] =~ /_spec\.rb/)
87
+ Thread.current['logger-output'] = []
88
+ logdevice = ArrayLogger.new( Thread.current['logger-output'] )
89
+ OpenLDAP.logger = Logger.new( logdevice )
90
+ # OpenLDAP.logger.level = level
91
+ OpenLDAP.logger.formatter = OpenLDAP::HtmlLogFormatter.new( OpenLDAP.logger )
92
+ end
93
+ end
94
+
95
+
96
+ ### Load the testing LDAP config options from a YAML file.
97
+ def load_ldap_config
98
+ unless defined?( @ldap_config ) && @ldap_config
99
+ if TEST_CONFIG_FILE.exist?
100
+ $stderr.puts "Loading LDAP config from #{TEST_CONFIG_FILE}" if $VERBOSE
101
+ @ldap_config = YAML.load( TEST_CONFIG_FILE.read )
102
+ else
103
+ $stderr.puts "Skipping tests that require access to a live directory. Copy the ",
104
+ "#{TEST_CONFIG_FILE}-example file and provide valid values for testing",
105
+ "with an actual LDAP."
106
+ @ldap_config = {}
107
+ end
108
+ end
109
+
110
+ return @ldap_config
111
+ end
112
+
113
+ end
114
+
115
+
116
+ ### Mock with Rspec
117
+ RSpec.configure do |config|
118
+ include OpenLDAP::TestConstants
119
+
120
+ config.mock_with :rspec
121
+ config.include( OpenLDAP::SpecHelpers )
122
+
123
+ config.filter_run_excluding( :ruby_1_9_only => true ) if vvec( RUBY_VERSION ) >= vvec( '1.9.0' )
124
+ config.filter_run_excluding( :with_ldap_server => true ) unless TEST_CONFIG_FILE.exist?
125
+ end
126
+
127
+ # vim: set nosta noet ts=4 sw=4:
128
+
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname( __FILE__ ).dirname.parent.parent
6
+ libdir = basedir + 'lib'
7
+
8
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
9
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
10
+ }
11
+
12
+ require 'uri'
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'openldap/connection'
16
+
17
+ describe OpenLDAP::Connection do
18
+
19
+ before( :all ) do
20
+ setup_logging( :fatal )
21
+ end
22
+
23
+ after( :all ) do
24
+ reset_logging()
25
+ end
26
+
27
+
28
+ it "can be created with an LDAP URI" do
29
+ conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
30
+ conn.uris.should == [ TEST_LDAP_URI ]
31
+ end
32
+
33
+ it "can be created with an LDAP URI String" do
34
+ conn = OpenLDAP::Connection.new( TEST_LDAP_URI.to_s )
35
+ conn.uris.should == [ TEST_LDAP_URI ]
36
+ end
37
+
38
+ it "can be created with several LDAP URIs" do
39
+ conn = OpenLDAP::Connection.new( TEST_LDAP_URI, TEST_LOCAL_LDAP_URI )
40
+ conn.uris.should == [ TEST_LDAP_URI, TEST_LOCAL_LDAP_URI ]
41
+ end
42
+
43
+ context "an instance" do
44
+
45
+ before( :each ) do
46
+ @conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
47
+ end
48
+
49
+ it "can set the cacert file used for TLS" do
50
+ @conn.tls_cacertfile = Pathname( '/etc/openssl/cacerts/ldap.pem' )
51
+ @conn.tls_cacertfile.should == '/etc/openssl/cacerts/ldap.pem'
52
+ end
53
+
54
+ it "can set the cacert directory used for TLS" do
55
+ @conn.tls_cacertdir = Pathname( '/etc/openssl/cacerts' )
56
+ @conn.tls_cacertdir.should == '/etc/openssl/cacerts'
57
+ end
58
+
59
+ it "can set the certificate file used for TLS" do
60
+ @conn.tls_certfile = Pathname( '/etc/openssl/host.pem' )
61
+ @conn.tls_certfile.should == '/etc/openssl/host.pem'
62
+ end
63
+
64
+
65
+ it "can set the cipher suite used for TLS" do
66
+ @conn.tls_cipher_suite = 'HIGH:MEDIUM:+SSLv2'
67
+ @conn.tls_cipher_suite.should == 'HIGH:MEDIUM:+SSLv2'
68
+ end
69
+
70
+ it "can set the CRL checking strategy used for TLS" do
71
+ @conn.tls_crlcheck = :all
72
+ @conn.tls_crlcheck.should == :all
73
+ end
74
+
75
+ it "can set the CRL file used for TLS" do
76
+ @conn.tls_crlfile = '/var/db/036D5FF52BA395A08BA2F780F9ADEAD905431757.crl'
77
+ @conn.tls_crlfile.should == '/var/db/036D5FF52BA395A08BA2F780F9ADEAD905431757.crl'
78
+ end
79
+
80
+ it "can set the dhfile used for TLS" do
81
+ @conn.tls_dhfile = '/etc/openssl/dh1024.pem'
82
+ @conn.tls_dhfile.should == '/etc/openssl/dh1024.pem'
83
+ end
84
+
85
+ it "can set the keyfile used for TLS" do
86
+ @conn.tls_keyfile = '/etc/openssl/host.key'
87
+ @conn.tls_keyfile.should == '/etc/openssl/host.key'
88
+ end
89
+
90
+ it "can set the package used for TLS in recent versions of libldap" do
91
+ if OpenLDAP.api_info[:vendor_name] == "OpenLDAP" &&
92
+ OpenLDAP.api_info[:vendor_version] >= 20426
93
+ @conn.tls_package.should == 'OpenSSL'
94
+ else
95
+ expect {
96
+ @conn.tls_package
97
+ }.to raise_error( NotImplementedError, /version of libldap/i )
98
+ end
99
+ end
100
+
101
+ it "can set the minimum protocol version used for TLS" do
102
+ @conn.tls_protocol_min = 11
103
+ @conn.tls_protocol_min.should == 11
104
+ end
105
+
106
+ it "can set the random_file used for TLS" do
107
+ # :TODO: Don't know how to test this appropriately.
108
+ # @conn.tls_random_file = '/dev/urandom'
109
+ @conn.tls_random_file.should be_nil()
110
+ end
111
+
112
+ it "can set the certificate verification strategy used for TLS" do
113
+ @conn.tls_require_cert = :demand
114
+ @conn.tls_require_cert.should == :demand
115
+ end
116
+
117
+ it "can be set to time out if the connect(2) takes longer than 2 seconds" do
118
+ @conn.network_timeout = 2.0
119
+ @conn.network_timeout.should == 2.0
120
+ end
121
+
122
+ it "can have its network timeout reset" do
123
+ @conn.network_timeout = nil
124
+ @conn.network_timeout.should be_nil()
125
+ end
126
+
127
+ it "can connect asynchronously" do
128
+ @conn.async_connect?.should be_false()
129
+ @conn.async_connect = true
130
+ @conn.async_connect?.should be_true()
131
+ end
132
+
133
+ it "can disable asynchronous connections after they've been enabled" do
134
+ @conn.async_connect = true
135
+ @conn.async_connect = false
136
+ @conn.async_connect?.should be_false()
137
+ end
138
+
139
+ end
140
+
141
+ context "an unconnected instance" do
142
+
143
+ before( :each ) do
144
+ @conn = OpenLDAP::Connection.new( TEST_LDAP_URI )
145
+ end
146
+
147
+ it "knows that its file-descriptor is nil" do
148
+ @conn.fdno.should be_nil()
149
+ end
150
+
151
+ end
152
+
153
+
154
+ it "can start TLS negotiation synchronously", :with_ldap_server => true do
155
+ config = load_ldap_config()
156
+ uri = config['uri'] or abort "No 'uri' in the test config!"
157
+
158
+ conn = OpenLDAP::Connection.new( uri )
159
+ conn.start_tls
160
+ end
161
+
162
+ context "a connected instance", :with_ldap_server => true do
163
+
164
+ before( :all ) do
165
+ config = load_ldap_config()
166
+ @uri = config['uri'] or abort "No 'uri' in the test config!"
167
+ end
168
+
169
+ before( :each ) do
170
+ @conn = OpenLDAP::Connection.new( @uri )
171
+ @conn.start_tls
172
+ end
173
+
174
+ it "knows what its file-descriptor is" do
175
+ @conn.fdno.should be_a( Fixnum )
176
+ @conn.fdno.should > 2 # Something past STDERR
177
+ end
178
+
179
+ it "can return an IO for selecting against its file-descriptor" do
180
+ @conn.socket.should be_an( IO )
181
+ @conn.socket.fileno.should == @conn.fdno
182
+ @conn.socket.should_not be_autoclose()
183
+ @conn.socket.should_not be_close_on_exec()
184
+ @conn.socket.should be_binmode()
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+