net-ldap 0.12.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Contributors.rdoc +1 -0
- data/History.rdoc +89 -0
- data/README.rdoc +19 -9
- data/lib/net/ber/ber_parser.rb +4 -4
- data/lib/net/ber/core_ext/array.rb +1 -1
- data/lib/net/ber/core_ext/integer.rb +1 -1
- data/lib/net/ber/core_ext/string.rb +1 -1
- data/lib/net/ber/core_ext.rb +6 -6
- data/lib/net/ber.rb +39 -9
- data/lib/net/ldap/auth_adapter/gss_spnego.rb +9 -8
- data/lib/net/ldap/auth_adapter/sasl.rb +6 -4
- data/lib/net/ldap/auth_adapter/simple.rb +1 -1
- data/lib/net/ldap/connection.rb +163 -50
- data/lib/net/ldap/dataset.rb +5 -5
- data/lib/net/ldap/dn.rb +13 -14
- data/lib/net/ldap/entry.rb +17 -7
- data/lib/net/ldap/error.rb +2 -25
- data/lib/net/ldap/filter.rb +15 -8
- data/lib/net/ldap/instrumentation.rb +2 -2
- data/lib/net/ldap/password.rb +7 -5
- data/lib/net/ldap/pdu.rb +27 -3
- data/lib/net/ldap/version.rb +1 -1
- data/lib/net/ldap.rb +209 -90
- data/lib/net/snmp.rb +19 -19
- data/lib/net-ldap.rb +1 -1
- metadata +30 -99
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -5
- data/.rubocop_todo.yml +0 -462
- data/.travis.yml +0 -31
- data/CONTRIBUTING.md +0 -54
- data/Gemfile +0 -2
- data/Rakefile +0 -23
- data/net-ldap.gemspec +0 -36
- data/script/changelog +0 -47
- data/script/install-openldap +0 -112
- data/script/package +0 -7
- data/script/release +0 -16
- data/test/ber/core_ext/test_array.rb +0 -22
- data/test/ber/core_ext/test_string.rb +0 -25
- data/test/ber/test_ber.rb +0 -145
- data/test/fixtures/cacert.pem +0 -20
- data/test/fixtures/openldap/memberof.ldif +0 -33
- data/test/fixtures/openldap/retcode.ldif +0 -76
- data/test/fixtures/openldap/slapd.conf.ldif +0 -67
- data/test/fixtures/seed.ldif +0 -374
- data/test/integration/test_add.rb +0 -28
- data/test/integration/test_ber.rb +0 -30
- data/test/integration/test_bind.rb +0 -34
- data/test/integration/test_delete.rb +0 -31
- data/test/integration/test_open.rb +0 -88
- data/test/integration/test_return_codes.rb +0 -38
- data/test/integration/test_search.rb +0 -77
- data/test/support/vm/openldap/.gitignore +0 -1
- data/test/support/vm/openldap/README.md +0 -32
- data/test/support/vm/openldap/Vagrantfile +0 -33
- data/test/test_auth_adapter.rb +0 -11
- data/test/test_dn.rb +0 -44
- data/test/test_entry.rb +0 -65
- data/test/test_filter.rb +0 -223
- data/test/test_filter_parser.rb +0 -24
- data/test/test_helper.rb +0 -66
- data/test/test_ldap.rb +0 -67
- data/test/test_ldap_connection.rb +0 -460
- data/test/test_ldif.rb +0 -104
- data/test/test_password.rb +0 -10
- data/test/test_rename.rb +0 -77
- data/test/test_search.rb +0 -39
- data/test/test_snmp.rb +0 -119
- data/test/test_ssl_ber.rb +0 -40
- data/test/testdata.ldif +0 -101
- data/testserver/ldapserver.rb +0 -210
- data/testserver/testdata.ldif +0 -101
data/lib/net/ldap/password.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- ruby encoding: utf-8 -*-
|
2
2
|
require 'digest/sha1'
|
3
|
+
require 'digest/sha2'
|
3
4
|
require 'digest/md5'
|
4
5
|
require 'base64'
|
5
6
|
require 'securerandom'
|
@@ -19,20 +20,21 @@ class Net::LDAP::Password
|
|
19
20
|
# * Should we provide sha1 as a synonym for sha1? I vote no because then
|
20
21
|
# should you also provide ssha1 for symmetry?
|
21
22
|
#
|
22
|
-
attribute_value = ""
|
23
23
|
def generate(type, str)
|
24
24
|
case type
|
25
25
|
when :md5
|
26
|
-
|
26
|
+
'{MD5}' + Base64.strict_encode64(Digest::MD5.digest(str))
|
27
27
|
when :sha
|
28
|
-
|
28
|
+
'{SHA}' + Base64.strict_encode64(Digest::SHA1.digest(str))
|
29
29
|
when :ssha
|
30
30
|
salt = SecureRandom.random_bytes(16)
|
31
|
-
|
31
|
+
'{SSHA}' + Base64.strict_encode64(Digest::SHA1.digest(str + salt) + salt)
|
32
|
+
when :ssha256
|
33
|
+
salt = SecureRandom.random_bytes(16)
|
34
|
+
'{SSHA256}' + Base64.strict_encode64(Digest::SHA256.digest(str + salt) + salt)
|
32
35
|
else
|
33
36
|
raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})"
|
34
37
|
end
|
35
|
-
return attribute_value
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
data/lib/net/ldap/pdu.rb
CHANGED
@@ -74,6 +74,7 @@ class Net::LDAP::PDU
|
|
74
74
|
attr_reader :search_referrals
|
75
75
|
attr_reader :search_parameters
|
76
76
|
attr_reader :bind_parameters
|
77
|
+
attr_reader :extended_response
|
77
78
|
|
78
79
|
##
|
79
80
|
# Returns RFC-2251 Controls if any.
|
@@ -120,9 +121,9 @@ class Net::LDAP::PDU
|
|
120
121
|
when UnbindRequest
|
121
122
|
parse_unbind_request(ber_object[1])
|
122
123
|
when ExtendedResponse
|
123
|
-
|
124
|
+
parse_extended_response(ber_object[1])
|
124
125
|
else
|
125
|
-
raise
|
126
|
+
raise Error.new("unknown pdu-type: #{@app_tag}")
|
126
127
|
end
|
127
128
|
|
128
129
|
parse_controls(ber_object[2]) if ber_object[2]
|
@@ -174,12 +175,35 @@ class Net::LDAP::PDU
|
|
174
175
|
@ldap_result = {
|
175
176
|
:resultCode => sequence[0],
|
176
177
|
:matchedDN => sequence[1],
|
177
|
-
:errorMessage => sequence[2]
|
178
|
+
:errorMessage => sequence[2],
|
178
179
|
}
|
179
180
|
parse_search_referral(sequence[3]) if @ldap_result[:resultCode] == Net::LDAP::ResultCodeReferral
|
180
181
|
end
|
181
182
|
private :parse_ldap_result
|
182
183
|
|
184
|
+
##
|
185
|
+
# Parse an extended response
|
186
|
+
#
|
187
|
+
# http://www.ietf.org/rfc/rfc2251.txt
|
188
|
+
#
|
189
|
+
# Each Extended operation consists of an Extended request and an
|
190
|
+
# Extended response.
|
191
|
+
#
|
192
|
+
# ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
193
|
+
# requestName [0] LDAPOID,
|
194
|
+
# requestValue [1] OCTET STRING OPTIONAL }
|
195
|
+
|
196
|
+
def parse_extended_response(sequence)
|
197
|
+
sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
|
198
|
+
@ldap_result = {
|
199
|
+
:resultCode => sequence[0],
|
200
|
+
:matchedDN => sequence[1],
|
201
|
+
:errorMessage => sequence[2],
|
202
|
+
}
|
203
|
+
@extended_response = sequence[3]
|
204
|
+
end
|
205
|
+
private :parse_extended_response
|
206
|
+
|
183
207
|
##
|
184
208
|
# A Bind Response may have an additional field, ID [7], serverSaslCreds,
|
185
209
|
# per RFC 2251 pgh 4.2.3.
|
data/lib/net/ldap/version.rb
CHANGED
data/lib/net/ldap.rb
CHANGED
@@ -17,19 +17,19 @@ module Net # :nodoc:
|
|
17
17
|
end
|
18
18
|
require 'socket'
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
require_relative 'ber'
|
21
|
+
require_relative 'ldap/pdu'
|
22
|
+
require_relative 'ldap/filter'
|
23
|
+
require_relative 'ldap/dataset'
|
24
|
+
require_relative 'ldap/password'
|
25
|
+
require_relative 'ldap/entry'
|
26
|
+
require_relative 'ldap/instrumentation'
|
27
|
+
require_relative 'ldap/connection'
|
28
|
+
require_relative 'ldap/version'
|
29
|
+
require_relative 'ldap/error'
|
30
|
+
require_relative 'ldap/auth_adapter'
|
31
|
+
require_relative 'ldap/auth_adapter/simple'
|
32
|
+
require_relative 'ldap/auth_adapter/sasl'
|
33
33
|
|
34
34
|
Net::LDAP::AuthAdapter.register([:simple, :anon, :anonymous], Net::LDAP::AuthAdapter::Simple)
|
35
35
|
Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
|
@@ -79,6 +79,14 @@ Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
|
|
79
79
|
#
|
80
80
|
# p ldap.get_operation_result
|
81
81
|
#
|
82
|
+
# === Setting connect timeout
|
83
|
+
#
|
84
|
+
# By default, Net::LDAP uses TCP sockets with a connection timeout of 5 seconds.
|
85
|
+
#
|
86
|
+
# This value can be tweaked passing the :connect_timeout parameter.
|
87
|
+
# i.e.
|
88
|
+
# ldap = Net::LDAP.new ...,
|
89
|
+
# :connect_timeout => 3
|
82
90
|
#
|
83
91
|
# == A Brief Introduction to LDAP
|
84
92
|
#
|
@@ -256,14 +264,14 @@ class Net::LDAP
|
|
256
264
|
SearchScope_BaseObject = 0
|
257
265
|
SearchScope_SingleLevel = 1
|
258
266
|
SearchScope_WholeSubtree = 2
|
259
|
-
SearchScopes = [
|
260
|
-
SearchScope_WholeSubtree
|
267
|
+
SearchScopes = [SearchScope_BaseObject, SearchScope_SingleLevel,
|
268
|
+
SearchScope_WholeSubtree]
|
261
269
|
|
262
270
|
DerefAliases_Never = 0
|
263
271
|
DerefAliases_Search = 1
|
264
272
|
DerefAliases_Find = 2
|
265
273
|
DerefAliases_Always = 3
|
266
|
-
DerefAliasesArray = [
|
274
|
+
DerefAliasesArray = [DerefAliases_Never, DerefAliases_Search, DerefAliases_Find, DerefAliases_Always]
|
267
275
|
|
268
276
|
primitive = { 2 => :null } # UnbindRequest body
|
269
277
|
constructed = {
|
@@ -315,7 +323,14 @@ class Net::LDAP
|
|
315
323
|
:constructed => constructed,
|
316
324
|
}
|
317
325
|
|
326
|
+
universal = {
|
327
|
+
constructed: {
|
328
|
+
107 => :array, #ExtendedResponse (PasswdModifyResponseValue)
|
329
|
+
},
|
330
|
+
}
|
331
|
+
|
318
332
|
AsnSyntax = Net::BER.compile_syntax(:application => application,
|
333
|
+
:universal => universal,
|
319
334
|
:context_specific => context_specific)
|
320
335
|
|
321
336
|
DefaultHost = "127.0.0.1"
|
@@ -324,7 +339,8 @@ class Net::LDAP
|
|
324
339
|
DefaultTreebase = "dc=com"
|
325
340
|
DefaultForceNoPage = false
|
326
341
|
|
327
|
-
StartTlsOid =
|
342
|
+
StartTlsOid = '1.3.6.1.4.1.1466.20037'
|
343
|
+
PasswdModifyOid = '1.3.6.1.4.1.4203.1.11.1'
|
328
344
|
|
329
345
|
# https://tools.ietf.org/html/rfc4511#section-4.1.9
|
330
346
|
# https://tools.ietf.org/html/rfc4511#appendix-A
|
@@ -373,14 +389,14 @@ class Net::LDAP
|
|
373
389
|
ResultCodeCompareFalse,
|
374
390
|
ResultCodeCompareTrue,
|
375
391
|
ResultCodeReferral,
|
376
|
-
ResultCodeSaslBindInProgress
|
392
|
+
ResultCodeSaslBindInProgress,
|
377
393
|
]
|
378
394
|
|
379
395
|
# nonstandard list of "successful" result codes for searches
|
380
396
|
ResultCodesSearchSuccess = [
|
381
397
|
ResultCodeSuccess,
|
382
398
|
ResultCodeTimeLimitExceeded,
|
383
|
-
ResultCodeSizeLimitExceeded
|
399
|
+
ResultCodeSizeLimitExceeded,
|
384
400
|
]
|
385
401
|
|
386
402
|
# map of result code to human message
|
@@ -422,7 +438,7 @@ class Net::LDAP
|
|
422
438
|
ResultCodeEntryAlreadyExists => "Entry Already Exists",
|
423
439
|
ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
|
424
440
|
ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
|
425
|
-
ResultCodeOther => "Other"
|
441
|
+
ResultCodeOther => "Other",
|
426
442
|
}
|
427
443
|
|
428
444
|
module LDAPControls
|
@@ -460,20 +476,73 @@ class Net::LDAP
|
|
460
476
|
# specify a treebase. If you give a treebase value in any particular
|
461
477
|
# call to #search, that value will override any treebase value you give
|
462
478
|
# here.
|
463
|
-
# * :encryption => specifies the encryption to be used in communicating
|
464
|
-
# with the LDAP server. The value is either a Hash containing additional
|
465
|
-
# parameters, or the Symbol :simple_tls, which is equivalent to
|
466
|
-
# specifying the Hash {:method => :simple_tls}. There is a fairly large
|
467
|
-
# range of potential values that may be given for this parameter. See
|
468
|
-
# #encryption for details.
|
469
479
|
# * :force_no_page => Set to true to prevent paged results even if your
|
470
480
|
# server says it supports them. This is a fix for MS Active Directory
|
471
481
|
# * :instrumentation_service => An object responsible for instrumenting
|
472
482
|
# operations, compatible with ActiveSupport::Notifications' public API.
|
483
|
+
# * :encryption => specifies the encryption to be used in communicating
|
484
|
+
# with the LDAP server. The value must be a Hash containing additional
|
485
|
+
# parameters, which consists of two keys:
|
486
|
+
# method: - :simple_tls or :start_tls
|
487
|
+
# tls_options: - Hash of options for that method
|
488
|
+
# The :simple_tls encryption method encrypts <i>all</i> communications
|
489
|
+
# with the LDAP server. It completely establishes SSL/TLS encryption with
|
490
|
+
# the LDAP server before any LDAP-protocol data is exchanged. There is no
|
491
|
+
# plaintext negotiation and no special encryption-request controls are
|
492
|
+
# sent to the server. <i>The :simple_tls option is the simplest, easiest
|
493
|
+
# way to encrypt communications between Net::LDAP and LDAP servers.</i>
|
494
|
+
# If you get communications or protocol errors when using this option,
|
495
|
+
# check with your LDAP server administrator. Pay particular attention
|
496
|
+
# to the TCP port you are connecting to. It's impossible for an LDAP
|
497
|
+
# server to support plaintext LDAP communications and <i>simple TLS</i>
|
498
|
+
# connections on the same port. The standard TCP port for unencrypted
|
499
|
+
# LDAP connections is 389, but the standard port for simple-TLS
|
500
|
+
# encrypted connections is 636. Be sure you are using the correct port.
|
501
|
+
# The :start_tls like the :simple_tls encryption method also encrypts all
|
502
|
+
# communcations with the LDAP server. With the exception that it operates
|
503
|
+
# over the standard TCP port.
|
504
|
+
#
|
505
|
+
# To validate the LDAP server's certificate (a security must if you're
|
506
|
+
# talking over the public internet), you need to set :tls_options
|
507
|
+
# something like this...
|
508
|
+
#
|
509
|
+
# Net::LDAP.new(
|
510
|
+
# # ... set host, bind dn, etc ...
|
511
|
+
# encryption: {
|
512
|
+
# method: :simple_tls,
|
513
|
+
# tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS,
|
514
|
+
# }
|
515
|
+
# )
|
516
|
+
#
|
517
|
+
# The above will use the operating system-provided store of CA
|
518
|
+
# certificates to validate your LDAP server's cert.
|
519
|
+
# If cert validation fails, it'll happen during the #bind
|
520
|
+
# whenever you first try to open a connection to the server.
|
521
|
+
# Those methods will throw Net::LDAP::ConnectionError with
|
522
|
+
# a message about certificate verify failing. If your
|
523
|
+
# LDAP server's certificate is signed by DigiCert, Comodo, etc.,
|
524
|
+
# you're probably good. If you've got a self-signed cert but it's
|
525
|
+
# been added to the host's OS-maintained CA store (e.g. on Debian
|
526
|
+
# add foobar.crt to /usr/local/share/ca-certificates/ and run
|
527
|
+
# `update-ca-certificates`), then the cert should pass validation.
|
528
|
+
# To ignore the OS's CA store, put your CA in a PEM-encoded file and...
|
529
|
+
#
|
530
|
+
# encryption: {
|
531
|
+
# method: :simple_tls,
|
532
|
+
# tls_options: { ca_file: '/path/to/my-little-ca.pem',
|
533
|
+
# ssl_version: 'TLSv1_1' },
|
534
|
+
# }
|
535
|
+
#
|
536
|
+
# As you might guess, the above example also fails the connection
|
537
|
+
# if the client can't negotiate TLS v1.1.
|
538
|
+
# tls_options is ultimately passed to OpenSSL::SSL::SSLContext#set_params
|
539
|
+
# For more details, see
|
540
|
+
# http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
|
473
541
|
#
|
474
542
|
# Instantiating a Net::LDAP object does <i>not</i> result in network
|
475
543
|
# traffic to the LDAP server. It simply stores the connection and binding
|
476
|
-
# parameters in the object.
|
544
|
+
# parameters in the object. That's why Net::LDAP.new doesn't throw
|
545
|
+
# cert validation errors itself; #bind does instead.
|
477
546
|
def initialize(args = {})
|
478
547
|
@host = args[:host] || DefaultHost
|
479
548
|
@port = args[:port] || DefaultPort
|
@@ -482,7 +551,8 @@ class Net::LDAP
|
|
482
551
|
@auth = args[:auth] || DefaultAuth
|
483
552
|
@base = args[:base] || DefaultTreebase
|
484
553
|
@force_no_page = args[:force_no_page] || DefaultForceNoPage
|
485
|
-
encryption args[:encryption] # may be nil
|
554
|
+
@encryption = normalize_encryption(args[:encryption]) # may be nil
|
555
|
+
@connect_timeout = args[:connect_timeout]
|
486
556
|
|
487
557
|
if pr = @auth[:password] and pr.respond_to?(:call)
|
488
558
|
@auth[:password] = pr.call
|
@@ -533,7 +603,7 @@ class Net::LDAP
|
|
533
603
|
@auth = {
|
534
604
|
:method => :simple,
|
535
605
|
:username => username,
|
536
|
-
:password => password
|
606
|
+
:password => password,
|
537
607
|
}
|
538
608
|
end
|
539
609
|
alias_method :auth, :authenticate
|
@@ -546,54 +616,12 @@ class Net::LDAP
|
|
546
616
|
# additional capabilities are added, more configuration values will be
|
547
617
|
# added here.
|
548
618
|
#
|
549
|
-
#
|
550
|
-
# with the LDAP server. It completely establishes SSL/TLS encryption with
|
551
|
-
# the LDAP server before any LDAP-protocol data is exchanged. There is no
|
552
|
-
# plaintext negotiation and no special encryption-request controls are
|
553
|
-
# sent to the server. <i>The :simple_tls option is the simplest, easiest
|
554
|
-
# way to encrypt communications between Net::LDAP and LDAP servers.</i>
|
555
|
-
# It's intended for cases where you have an implicit level of trust in the
|
556
|
-
# authenticity of the LDAP server. No validation of the LDAP server's SSL
|
557
|
-
# certificate is performed. This means that :simple_tls will not produce
|
558
|
-
# errors if the LDAP server's encryption certificate is not signed by a
|
559
|
-
# well-known Certification Authority. If you get communications or
|
560
|
-
# protocol errors when using this option, check with your LDAP server
|
561
|
-
# administrator. Pay particular attention to the TCP port you are
|
562
|
-
# connecting to. It's impossible for an LDAP server to support plaintext
|
563
|
-
# LDAP communications and <i>simple TLS</i> connections on the same port.
|
564
|
-
# The standard TCP port for unencrypted LDAP connections is 389, but the
|
565
|
-
# standard port for simple-TLS encrypted connections is 636. Be sure you
|
566
|
-
# are using the correct port.
|
567
|
-
#
|
568
|
-
# The :start_tls like the :simple_tls encryption method also encrypts all
|
569
|
-
# communcations with the LDAP server. With the exception that it operates
|
570
|
-
# over the standard TCP port.
|
571
|
-
#
|
572
|
-
# In order to verify certificates and enable other TLS options, the
|
573
|
-
# :tls_options hash can be passed alongside :simple_tls or :start_tls.
|
574
|
-
# This hash contains any options that can be passed to
|
575
|
-
# OpenSSL::SSL::SSLContext#set_params(). The most common options passed
|
576
|
-
# should be OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, or the :ca_file option,
|
577
|
-
# which contains a path to a Certificate Authority file (PEM-encoded).
|
578
|
-
#
|
579
|
-
# Example for a default setup without custom settings:
|
580
|
-
# {
|
581
|
-
# :method => :simple_tls,
|
582
|
-
# :tls_options => OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
583
|
-
# }
|
584
|
-
#
|
585
|
-
# Example for specifying a CA-File and only allowing TLSv1.1 connections:
|
619
|
+
# This method is deprecated.
|
586
620
|
#
|
587
|
-
# {
|
588
|
-
# :method => :start_tls,
|
589
|
-
# :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
|
590
|
-
# }
|
591
621
|
def encryption(args)
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
end
|
596
|
-
@encryption = args
|
622
|
+
warn "Deprecation warning: please give :encryption option as a Hash to Net::LDAP.new"
|
623
|
+
return if args.nil?
|
624
|
+
@encryption = normalize_encryption(args)
|
597
625
|
end
|
598
626
|
|
599
627
|
# #open takes the same parameters as #new. #open makes a network
|
@@ -637,8 +665,11 @@ class Net::LDAP
|
|
637
665
|
#++
|
638
666
|
def get_operation_result
|
639
667
|
result = @result
|
640
|
-
result = result.result if result.is_a?(Net::LDAP::PDU)
|
641
668
|
os = OpenStruct.new
|
669
|
+
if result.is_a?(Net::LDAP::PDU)
|
670
|
+
os.extended_response = result.extended_response
|
671
|
+
result = result.result
|
672
|
+
end
|
642
673
|
if result.is_a?(Hash)
|
643
674
|
# We might get a hash of LDAP response codes instead of a simple
|
644
675
|
# numeric code.
|
@@ -681,7 +712,7 @@ class Net::LDAP
|
|
681
712
|
begin
|
682
713
|
@open_connection = new_connection
|
683
714
|
payload[:connection] = @open_connection
|
684
|
-
payload[:bind] = @open_connection.bind(@auth)
|
715
|
+
payload[:bind] = @result = @open_connection.bind(@auth)
|
685
716
|
yield self
|
686
717
|
ensure
|
687
718
|
@open_connection.close if @open_connection
|
@@ -750,10 +781,10 @@ class Net::LDAP
|
|
750
781
|
|
751
782
|
instrument "search.net_ldap", args do |payload|
|
752
783
|
@result = use_connection(args) do |conn|
|
753
|
-
conn.search(args)
|
784
|
+
conn.search(args) do |entry|
|
754
785
|
result_set << entry if result_set
|
755
786
|
yield entry if block_given?
|
756
|
-
|
787
|
+
end
|
757
788
|
end
|
758
789
|
|
759
790
|
if return_result_set
|
@@ -892,7 +923,7 @@ class Net::LDAP
|
|
892
923
|
# end
|
893
924
|
def bind_as(args = {})
|
894
925
|
result = false
|
895
|
-
open
|
926
|
+
open do |me|
|
896
927
|
rs = search args
|
897
928
|
if rs and rs.first and dn = rs.first.dn
|
898
929
|
password = args[:password]
|
@@ -900,7 +931,7 @@ class Net::LDAP
|
|
900
931
|
result = rs if bind(:method => :simple, :username => dn,
|
901
932
|
:password => password)
|
902
933
|
end
|
903
|
-
|
934
|
+
end
|
904
935
|
result
|
905
936
|
end
|
906
937
|
|
@@ -1027,6 +1058,44 @@ class Net::LDAP
|
|
1027
1058
|
end
|
1028
1059
|
end
|
1029
1060
|
|
1061
|
+
# Password Modify
|
1062
|
+
#
|
1063
|
+
# Change existing password:
|
1064
|
+
#
|
1065
|
+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
|
1066
|
+
# auth = {
|
1067
|
+
# method: :simple,
|
1068
|
+
# username: dn,
|
1069
|
+
# password: 'passworD1'
|
1070
|
+
# }
|
1071
|
+
# ldap.password_modify(dn: dn,
|
1072
|
+
# auth: auth,
|
1073
|
+
# old_password: 'passworD1',
|
1074
|
+
# new_password: 'passworD2')
|
1075
|
+
#
|
1076
|
+
# Or get the LDAP server to generate a password for you:
|
1077
|
+
#
|
1078
|
+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
|
1079
|
+
# auth = {
|
1080
|
+
# method: :simple,
|
1081
|
+
# username: dn,
|
1082
|
+
# password: 'passworD1'
|
1083
|
+
# }
|
1084
|
+
# ldap.password_modify(dn: dn,
|
1085
|
+
# auth: auth,
|
1086
|
+
# old_password: 'passworD1')
|
1087
|
+
#
|
1088
|
+
# ldap.get_operation_result.extended_response[0][0] #=> 'VtcgGf/G'
|
1089
|
+
#
|
1090
|
+
def password_modify(args)
|
1091
|
+
instrument "modify_password.net_ldap", args do |payload|
|
1092
|
+
@result = use_connection(args) do |conn|
|
1093
|
+
conn.password_modify(args)
|
1094
|
+
end
|
1095
|
+
@result.success?
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
|
1030
1099
|
# Add a value to an attribute. Takes the full DN of the entry to modify,
|
1031
1100
|
# the name (Symbol or String) of the attribute, and the value (String or
|
1032
1101
|
# Array). If the attribute does not exist (and there are no schema
|
@@ -1113,14 +1182,22 @@ class Net::LDAP
|
|
1113
1182
|
# entries. This method sends an extra control code to tell the LDAP server
|
1114
1183
|
# to do a tree delete. ('1.2.840.113556.1.4.805')
|
1115
1184
|
#
|
1185
|
+
# If the LDAP server does not support the DELETE_TREE control code, subordinate
|
1186
|
+
# entries are deleted recursively instead.
|
1187
|
+
#
|
1116
1188
|
# Returns True or False to indicate whether the delete succeeded. Extended
|
1117
1189
|
# status information is available by calling #get_operation_result.
|
1118
1190
|
#
|
1119
1191
|
# dn = "mail=deleteme@example.com, ou=people, dc=example, dc=com"
|
1120
1192
|
# ldap.delete_tree :dn => dn
|
1121
1193
|
def delete_tree(args)
|
1122
|
-
|
1194
|
+
if search_root_dse[:supportedcontrol].include? Net::LDAP::LDAPControls::DELETE_TREE
|
1195
|
+
delete(args.merge(:control_codes => [[Net::LDAP::LDAPControls::DELETE_TREE, true]]))
|
1196
|
+
else
|
1197
|
+
recursive_delete(args)
|
1198
|
+
end
|
1123
1199
|
end
|
1200
|
+
|
1124
1201
|
# This method is experimental and subject to change. Return the rootDSE
|
1125
1202
|
# record from the LDAP server as a Net::LDAP::Entry, or an empty Entry if
|
1126
1203
|
# the server doesn't return the record.
|
@@ -1145,7 +1222,7 @@ class Net::LDAP
|
|
1145
1222
|
:supportedExtension,
|
1146
1223
|
:supportedFeatures,
|
1147
1224
|
:supportedLdapVersion,
|
1148
|
-
:supportedSASLMechanisms
|
1225
|
+
:supportedSASLMechanisms,
|
1149
1226
|
])
|
1150
1227
|
(rs and rs.first) or Net::LDAP::Entry.new
|
1151
1228
|
end
|
@@ -1212,6 +1289,11 @@ class Net::LDAP
|
|
1212
1289
|
inspected
|
1213
1290
|
end
|
1214
1291
|
|
1292
|
+
# Internal: Set @open_connection for testing
|
1293
|
+
def connection=(connection)
|
1294
|
+
@open_connection = connection
|
1295
|
+
end
|
1296
|
+
|
1215
1297
|
private
|
1216
1298
|
|
1217
1299
|
# Yields an open connection if there is one, otherwise establishes a new
|
@@ -1224,11 +1306,9 @@ class Net::LDAP
|
|
1224
1306
|
else
|
1225
1307
|
begin
|
1226
1308
|
conn = new_connection
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
return result
|
1231
|
-
end
|
1309
|
+
result = conn.bind(args[:auth] || @auth)
|
1310
|
+
return result unless result.result_code == Net::LDAP::ResultCodeSuccess
|
1311
|
+
yield conn
|
1232
1312
|
ensure
|
1233
1313
|
conn.close if conn
|
1234
1314
|
end
|
@@ -1237,11 +1317,50 @@ class Net::LDAP
|
|
1237
1317
|
|
1238
1318
|
# Establish a new connection to the LDAP server
|
1239
1319
|
def new_connection
|
1240
|
-
Net::LDAP::Connection.new \
|
1320
|
+
connection = Net::LDAP::Connection.new \
|
1241
1321
|
:host => @host,
|
1242
1322
|
:port => @port,
|
1243
1323
|
:hosts => @hosts,
|
1244
1324
|
:encryption => @encryption,
|
1245
|
-
:instrumentation_service => @instrumentation_service
|
1325
|
+
:instrumentation_service => @instrumentation_service,
|
1326
|
+
:connect_timeout => @connect_timeout
|
1327
|
+
|
1328
|
+
# Force connect to see if there's a connection error
|
1329
|
+
connection.socket
|
1330
|
+
connection
|
1331
|
+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
1332
|
+
@result = {
|
1333
|
+
:resultCode => 52,
|
1334
|
+
:errorMessage => ResultStrings[ResultCodeUnavailable],
|
1335
|
+
}
|
1336
|
+
raise e
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
# Normalize encryption parameter the constructor accepts, expands a few
|
1340
|
+
# convenience symbols into recognizable hashes
|
1341
|
+
def normalize_encryption(args)
|
1342
|
+
return if args.nil?
|
1343
|
+
return args if args.is_a? Hash
|
1344
|
+
|
1345
|
+
case method = args.to_sym
|
1346
|
+
when :simple_tls, :start_tls
|
1347
|
+
{ :method => method, :tls_options => {} }
|
1348
|
+
end
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
# Recursively delete a dn and it's subordinate children.
|
1352
|
+
# This is useful when a server does not support the DELETE_TREE control code.
|
1353
|
+
def recursive_delete(args)
|
1354
|
+
raise EmptyDNError unless args.is_a?(Hash) && args.key?(:dn)
|
1355
|
+
# Delete Children
|
1356
|
+
search(base: args[:dn], scope: Net::LDAP::SearchScope_SingleLevel) do |entry|
|
1357
|
+
recursive_delete(dn: entry.dn)
|
1358
|
+
end
|
1359
|
+
# Delete Self
|
1360
|
+
unless delete(dn: args[:dn])
|
1361
|
+
raise Net::LDAP::Error, get_operation_result[:error_message].to_s
|
1362
|
+
end
|
1363
|
+
true
|
1246
1364
|
end
|
1365
|
+
|
1247
1366
|
end # class LDAP
|