net-ldap 0.3.1 → 0.5.1
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.
Potentially problematic release.
This version of net-ldap might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.travis.yml +7 -0
- data/Contributors.rdoc +1 -0
- data/Gemfile +2 -0
- data/History.rdoc +12 -0
- data/Manifest.txt +6 -0
- data/README.rdoc +2 -7
- data/Rakefile +5 -3
- data/lib/net/ber.rb +4 -2
- data/lib/net/ber/core_ext/array.rb +14 -0
- data/lib/net/ber/core_ext/string.rb +22 -4
- data/lib/net/ldap.rb +125 -43
- data/lib/net/ldap/filter.rb +30 -8
- data/lib/net/ldap/password.rb +20 -14
- data/lib/net/ldap/pdu.rb +16 -0
- data/lib/net/ldap/version.rb +5 -0
- data/lib/net/snmp.rb +3 -1
- data/net-ldap.gemspec +14 -9
- data/spec/unit/ber/ber_spec.rb +34 -2
- data/spec/unit/ber/core_ext/array_spec.rb +24 -0
- data/spec/unit/ldap/filter_parser_spec.rb +20 -0
- data/spec/unit/ldap/filter_spec.rb +31 -0
- data/spec/unit/ldap/search_spec.rb +35 -0
- data/spec/unit/ldap_spec.rb +37 -7
- metadata +83 -64
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 38e2a66893eef1d93e64fda3d84dc70e51b504f2
|
4
|
+
data.tar.gz: e15b5c72e9fd9a38fcf058a9c432e77320d3fcb7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6bae92818e1c80d15b731d5f8b28ec39f332a0359e8eed3c704e7b4606d5c3ce8f29a7a46bb511b2963a2c30f8085529431a1bcef6780db09f018dd61049ff74
|
7
|
+
data.tar.gz: 7277e94a5fcd96fa2cf53e32727ac67b7a8feef5fb7f12190b5fc4dcf1c1c07d0f62a398daa726d776c988372308b1e964a54b0241312515d243a5f88709be41
|
data/.travis.yml
ADDED
data/Contributors.rdoc
CHANGED
data/Gemfile
ADDED
data/History.rdoc
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== Net::LDAP 0.5.0 / 2013-07-22
|
2
|
+
* Major changes:
|
3
|
+
* Required Ruby version is >=1.9.3
|
4
|
+
* Major enhancements:
|
5
|
+
* Added alias dereferencing (@ngwilson)
|
6
|
+
* BER now unescapes characters that are already escaped in the source string (@jzinn)
|
7
|
+
* BerIdentifiedString will now fall back to ASCII-8 encoding if the source Ruby object cannot be encoded in UTF-8 (@lfu)
|
8
|
+
* Bug fixes:
|
9
|
+
* Fixed nil variable error when following a reference response (@cmdrclueless)
|
10
|
+
* Fixed FilterParser unable to parse multibyte strings (@satoryu)
|
11
|
+
* Return ConverterNotFound when dealing with a potentially corrupt data response (@jamuc)
|
12
|
+
|
1
13
|
=== Net::LDAP 0.3.1 / 2012-02-15
|
2
14
|
* Bug Fixes:
|
3
15
|
* Bundler should now work again
|
data/Manifest.txt
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
.autotest
|
2
2
|
.rspec
|
3
|
+
.travis.yml
|
3
4
|
Contributors.rdoc
|
5
|
+
Gemfile
|
4
6
|
Hacking.rdoc
|
5
7
|
History.rdoc
|
6
8
|
License.rdoc
|
@@ -25,16 +27,20 @@ lib/net/ldap/entry.rb
|
|
25
27
|
lib/net/ldap/filter.rb
|
26
28
|
lib/net/ldap/password.rb
|
27
29
|
lib/net/ldap/pdu.rb
|
30
|
+
lib/net/ldap/version.rb
|
28
31
|
lib/net/snmp.rb
|
29
32
|
net-ldap.gemspec
|
30
33
|
spec/integration/ssl_ber_spec.rb
|
31
34
|
spec/spec.opts
|
32
35
|
spec/spec_helper.rb
|
33
36
|
spec/unit/ber/ber_spec.rb
|
37
|
+
spec/unit/ber/core_ext/array_spec.rb
|
34
38
|
spec/unit/ber/core_ext/string_spec.rb
|
35
39
|
spec/unit/ldap/dn_spec.rb
|
36
40
|
spec/unit/ldap/entry_spec.rb
|
41
|
+
spec/unit/ldap/filter_parser_spec.rb
|
37
42
|
spec/unit/ldap/filter_spec.rb
|
43
|
+
spec/unit/ldap/search_spec.rb
|
38
44
|
spec/unit/ldap_spec.rb
|
39
45
|
test/common.rb
|
40
46
|
test/test_entry.rb
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Net::LDAP for Ruby
|
1
|
+
= Net::LDAP for Ruby {<img src="https://travis-ci.org/ruby-ldap/ruby-net-ldap.png" />}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
|
2
2
|
|
3
3
|
== Description
|
4
4
|
|
@@ -30,7 +30,7 @@ See Net::LDAP for documentation and usage samples.
|
|
30
30
|
|
31
31
|
== Requirements
|
32
32
|
|
33
|
-
Net::LDAP requires a Ruby 1.
|
33
|
+
Net::LDAP requires a Ruby 1.9.3 compatible interpreter or better.
|
34
34
|
|
35
35
|
== Install
|
36
36
|
|
@@ -42,11 +42,6 @@ sources.
|
|
42
42
|
|
43
43
|
Simply require either 'net-ldap' or 'net/ldap'.
|
44
44
|
|
45
|
-
For non-RubyGems installations of Net::LDAP, you can use Minero Aoki's
|
46
|
-
{setup.rb}[http://i.loveruby.net/en/projects/setup/] as the layout of
|
47
|
-
Net::LDAP is compliant. The setup installer is not included in the
|
48
|
-
Net::LDAP repository.
|
49
|
-
|
50
45
|
:include: Contributors.rdoc
|
51
46
|
|
52
47
|
:include: License.rdoc
|
data/Rakefile
CHANGED
@@ -8,18 +8,20 @@ Hoe.plugin :git
|
|
8
8
|
Hoe.plugin :gemspec
|
9
9
|
|
10
10
|
Hoe.spec 'net-ldap' do |spec|
|
11
|
-
spec.rubyforge_name = spec.name
|
11
|
+
# spec.rubyforge_name = spec.name
|
12
12
|
|
13
13
|
spec.developer("Francis Cianfrocca", "blackhedd@rubyforge.org")
|
14
14
|
spec.developer("Emiel van de Laar", "gemiel@gmail.com")
|
15
15
|
spec.developer("Rory O'Connell", "rory.ocon@gmail.com")
|
16
16
|
spec.developer("Kaspar Schiess", "kaspar.schiess@absurd.li")
|
17
17
|
spec.developer("Austin Ziegler", "austin@rubyforge.org")
|
18
|
+
spec.developer("Michael Schaarschmidt", "michael@schaaryworks.com")
|
18
19
|
|
19
20
|
spec.remote_rdoc_dir = ''
|
20
21
|
spec.rsync_args << ' --exclude=statsvn/'
|
21
22
|
|
22
|
-
spec.
|
23
|
+
spec.urls = %w(http://rubyldap.com/' 'https://github.com/ruby-ldap/ruby-net-ldap)
|
24
|
+
spec.licenses = ['MIT']
|
23
25
|
|
24
26
|
spec.history_file = 'History.rdoc'
|
25
27
|
spec.readme_file = 'README.rdoc'
|
@@ -29,7 +31,7 @@ Hoe.spec 'net-ldap' do |spec|
|
|
29
31
|
spec.extra_dev_deps << [ "hoe-git", "~> 1" ]
|
30
32
|
spec.extra_dev_deps << [ "hoe-gemspec", "~> 1" ]
|
31
33
|
spec.extra_dev_deps << [ "metaid", "~> 1" ]
|
32
|
-
spec.extra_dev_deps << [ "flexmock", "
|
34
|
+
spec.extra_dev_deps << [ "flexmock", ">= 1.3.0" ]
|
33
35
|
spec.extra_dev_deps << [ "rspec", "~> 2.0" ]
|
34
36
|
|
35
37
|
spec.clean_globs << "coverage"
|
data/lib/net/ber.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- ruby encoding: utf-8 -*-
|
2
|
+
require 'net/ldap/version'
|
3
|
+
|
2
4
|
module Net # :nodoc:
|
3
5
|
##
|
4
6
|
# == Basic Encoding Rules (BER) Support Module
|
@@ -106,7 +108,7 @@ module Net # :nodoc:
|
|
106
108
|
# <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
|
107
109
|
# </table>
|
108
110
|
module BER
|
109
|
-
VERSION =
|
111
|
+
VERSION = Net::LDAP::VERSION
|
110
112
|
|
111
113
|
##
|
112
114
|
# Used for BER-encoding the length and content bytes of a Fixnum integer
|
@@ -296,7 +298,7 @@ class Net::BER::BerIdentifiedString < String
|
|
296
298
|
def initialize args
|
297
299
|
super args
|
298
300
|
# LDAP uses UTF-8 encoded strings
|
299
|
-
|
301
|
+
self.encode('UTF-8') if self.respond_to?(:encoding) rescue self
|
300
302
|
end
|
301
303
|
end
|
302
304
|
|
@@ -79,4 +79,18 @@ module Net::BER::Extensions::Array
|
|
79
79
|
oid = ary.pack("w*")
|
80
80
|
[6, oid.length].pack("CC") + oid
|
81
81
|
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Converts an array into a set of ber control codes
|
85
|
+
# The expected format is [[control_oid, criticality, control_value(optional)]]
|
86
|
+
# [['1.2.840.113556.1.4.805',true]]
|
87
|
+
#
|
88
|
+
def to_ber_control
|
89
|
+
#if our array does not contain at least one array then wrap it in an array before going forward
|
90
|
+
ary = self[0].kind_of?(Array) ? self : [self]
|
91
|
+
ary = ary.collect do |control_sequence|
|
92
|
+
control_sequence.collect{|element| element.to_ber}.to_ber_sequence.reject_empty_ber_arrays
|
93
|
+
end
|
94
|
+
ary.to_ber_sequence.reject_empty_ber_arrays
|
95
|
+
end
|
82
96
|
end
|
@@ -16,11 +16,25 @@ module Net::BER::Extensions::String
|
|
16
16
|
[code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
|
17
17
|
end
|
18
18
|
|
19
|
+
##
|
20
|
+
# Converts a string to a BER string but does *not* encode to UTF-8 first.
|
21
|
+
# This is required for proper representation of binary data for Microsoft
|
22
|
+
# Active Directory
|
23
|
+
def to_ber_bin(code = 0x04)
|
24
|
+
[code].pack('C') + length.to_ber_length_encoding + self
|
25
|
+
end
|
26
|
+
|
19
27
|
def raw_utf8_encoded
|
20
28
|
if self.respond_to?(:encode)
|
21
29
|
# Strings should be UTF-8 encoded according to LDAP.
|
22
30
|
# However, the BER code is not necessarily valid UTF-8
|
23
|
-
|
31
|
+
begin
|
32
|
+
self.encode('UTF-8').force_encoding('ASCII-8BIT')
|
33
|
+
rescue Encoding::UndefinedConversionError
|
34
|
+
self
|
35
|
+
rescue Encoding::ConverterNotFoundError
|
36
|
+
return self
|
37
|
+
end
|
24
38
|
else
|
25
39
|
self
|
26
40
|
end
|
@@ -46,15 +60,19 @@ module Net::BER::Extensions::String
|
|
46
60
|
def read_ber(syntax = nil)
|
47
61
|
StringIO.new(self).read_ber(syntax)
|
48
62
|
end
|
49
|
-
|
63
|
+
|
50
64
|
##
|
51
|
-
# Destructively reads a BER object from the string.
|
65
|
+
# Destructively reads a BER object from the string.
|
52
66
|
def read_ber!(syntax = nil)
|
53
67
|
io = StringIO.new(self)
|
54
68
|
|
55
69
|
result = io.read_ber(syntax)
|
56
70
|
self.slice!(0...io.pos)
|
57
|
-
|
71
|
+
|
58
72
|
return result
|
59
73
|
end
|
74
|
+
|
75
|
+
def reject_empty_ber_arrays
|
76
|
+
self.gsub(/0\000/n,'')
|
77
|
+
end
|
60
78
|
end
|
data/lib/net/ldap.rb
CHANGED
@@ -23,6 +23,7 @@ require 'net/ldap/filter'
|
|
23
23
|
require 'net/ldap/dataset'
|
24
24
|
require 'net/ldap/password'
|
25
25
|
require 'net/ldap/entry'
|
26
|
+
require 'net/ldap/version'
|
26
27
|
|
27
28
|
# == Quick-start for the Impatient
|
28
29
|
# === Quick Example of a user-authentication against an LDAP directory:
|
@@ -241,7 +242,6 @@ require 'net/ldap/entry'
|
|
241
242
|
# and then keeps it open while it executes a user-supplied block.
|
242
243
|
# Net::LDAP#open closes the connection on completion of the block.
|
243
244
|
class Net::LDAP
|
244
|
-
VERSION = "0.3.1"
|
245
245
|
|
246
246
|
class LdapError < StandardError; end
|
247
247
|
|
@@ -251,6 +251,12 @@ class Net::LDAP
|
|
251
251
|
SearchScopes = [ SearchScope_BaseObject, SearchScope_SingleLevel,
|
252
252
|
SearchScope_WholeSubtree ]
|
253
253
|
|
254
|
+
DerefAliases_Never = 0
|
255
|
+
DerefAliases_Search = 1
|
256
|
+
DerefAliases_Find = 2
|
257
|
+
DerefAliases_Always = 3
|
258
|
+
DerefAliasesArray = [ DerefAliases_Never, DerefAliases_Search, DerefAliases_Find, DerefAliases_Always ]
|
259
|
+
|
254
260
|
primitive = { 2 => :null } # UnbindRequest body
|
255
261
|
constructed = {
|
256
262
|
0 => :array, # BindRequest
|
@@ -308,6 +314,7 @@ class Net::LDAP
|
|
308
314
|
DefaultPort = 389
|
309
315
|
DefaultAuth = { :method => :anonymous }
|
310
316
|
DefaultTreebase = "dc=com"
|
317
|
+
DefaultForceNoPage = false
|
311
318
|
|
312
319
|
StartTlsOid = "1.3.6.1.4.1.1466.20037"
|
313
320
|
|
@@ -322,6 +329,7 @@ class Net::LDAP
|
|
322
329
|
14 => "saslBindInProgress",
|
323
330
|
16 => "No Such Attribute",
|
324
331
|
17 => "Undefined Attribute Type",
|
332
|
+
19 => "Constraint Violation",
|
325
333
|
20 => "Attribute or Value Exists",
|
326
334
|
32 => "No Such Object",
|
327
335
|
34 => "Invalid DN Syntax",
|
@@ -335,8 +343,11 @@ class Net::LDAP
|
|
335
343
|
68 => "Entry Already Exists"
|
336
344
|
}
|
337
345
|
|
338
|
-
module
|
339
|
-
|
346
|
+
module LDAPControls
|
347
|
+
PAGED_RESULTS = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
|
348
|
+
SORT_REQUEST = "1.2.840.113556.1.4.473"
|
349
|
+
SORT_RESPONSE = "1.2.840.113556.1.4.474"
|
350
|
+
DELETE_TREE = "1.2.840.113556.1.4.805"
|
340
351
|
end
|
341
352
|
|
342
353
|
def self.result2string(code) #:nodoc:
|
@@ -370,6 +381,8 @@ class Net::LDAP
|
|
370
381
|
# specifying the Hash {:method => :simple_tls}. There is a fairly large
|
371
382
|
# range of potential values that may be given for this parameter. See
|
372
383
|
# #encryption for details.
|
384
|
+
# * :force_no_page => Set to true to prevent paged results even if your
|
385
|
+
# server says it supports them. This is a fix for MS Active Directory
|
373
386
|
#
|
374
387
|
# Instantiating a Net::LDAP object does <i>not</i> result in network
|
375
388
|
# traffic to the LDAP server. It simply stores the connection and binding
|
@@ -380,6 +393,7 @@ class Net::LDAP
|
|
380
393
|
@verbose = false # Make this configurable with a switch on the class.
|
381
394
|
@auth = args[:auth] || DefaultAuth
|
382
395
|
@base = args[:base] || DefaultTreebase
|
396
|
+
@force_no_page = args[:force_no_page] || DefaultForceNoPage
|
383
397
|
encryption args[:encryption] # may be nil
|
384
398
|
|
385
399
|
if pr = @auth[:password] and pr.respond_to?(:call)
|
@@ -516,15 +530,17 @@ class Net::LDAP
|
|
516
530
|
# response codes instead of a simple numeric code.
|
517
531
|
#++
|
518
532
|
def get_operation_result
|
533
|
+
result = @result
|
534
|
+
result = result.result if result.is_a?(Net::LDAP::PDU)
|
519
535
|
os = OpenStruct.new
|
520
|
-
if
|
536
|
+
if result.is_a?(Hash)
|
521
537
|
# We might get a hash of LDAP response codes instead of a simple
|
522
538
|
# numeric code.
|
523
|
-
os.code = (
|
524
|
-
os.error_message =
|
525
|
-
os.matched_dn =
|
526
|
-
elsif
|
527
|
-
os.code =
|
539
|
+
os.code = (result[:resultCode] || "").to_i
|
540
|
+
os.error_message = result[:errorMessage]
|
541
|
+
os.matched_dn = result[:matchedDN]
|
542
|
+
elsif result
|
543
|
+
os.code = result
|
528
544
|
else
|
529
545
|
os.code = 0
|
530
546
|
end
|
@@ -582,6 +598,8 @@ class Net::LDAP
|
|
582
598
|
# Net::LDAP::SearchScope_WholeSubtree. Default is WholeSubtree.)
|
583
599
|
# * :size (an integer indicating the maximum number of search entries to
|
584
600
|
# return. Default is zero, which signifies no limit.)
|
601
|
+
# * :deref (one of: Net::LDAP::DerefAliases_Never, Net::LDAP::DerefAliases_Search,
|
602
|
+
# Net::LDAP::DerefAliases_Find, Net::LDAP::DerefAliases_Always. Default is Never.)
|
585
603
|
#
|
586
604
|
# #search queries the LDAP server and passes <i>each entry</i> to the
|
587
605
|
# caller-supplied block, as an object of type Net::LDAP::Entry. If the
|
@@ -629,11 +647,10 @@ class Net::LDAP
|
|
629
647
|
yield entry if block_given?
|
630
648
|
}
|
631
649
|
else
|
632
|
-
@result = 0
|
633
650
|
begin
|
634
651
|
conn = Net::LDAP::Connection.new(:host => @host, :port => @port,
|
635
652
|
:encryption => @encryption)
|
636
|
-
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
653
|
+
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
|
637
654
|
@result = conn.search(args) { |entry|
|
638
655
|
result_set << entry if result_set
|
639
656
|
yield entry if block_given?
|
@@ -645,9 +662,9 @@ class Net::LDAP
|
|
645
662
|
end
|
646
663
|
|
647
664
|
if return_result_set
|
648
|
-
@result == 0 ? result_set : nil
|
665
|
+
(!@result.nil? && @result.result_code == 0) ? result_set : nil
|
649
666
|
else
|
650
|
-
@result
|
667
|
+
@result.success?
|
651
668
|
end
|
652
669
|
end
|
653
670
|
|
@@ -721,7 +738,7 @@ class Net::LDAP
|
|
721
738
|
end
|
722
739
|
end
|
723
740
|
|
724
|
-
@result
|
741
|
+
@result.success?
|
725
742
|
end
|
726
743
|
|
727
744
|
# #bind_as is for testing authentication credentials.
|
@@ -816,14 +833,14 @@ class Net::LDAP
|
|
816
833
|
begin
|
817
834
|
conn = Connection.new(:host => @host, :port => @port,
|
818
835
|
:encryption => @encryption)
|
819
|
-
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
836
|
+
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
|
820
837
|
@result = conn.add(args)
|
821
838
|
end
|
822
839
|
ensure
|
823
840
|
conn.close if conn
|
824
841
|
end
|
825
842
|
end
|
826
|
-
@result
|
843
|
+
@result.success?
|
827
844
|
end
|
828
845
|
|
829
846
|
# Modifies the attribute values of a particular entry on the LDAP
|
@@ -914,14 +931,15 @@ class Net::LDAP
|
|
914
931
|
begin
|
915
932
|
conn = Connection.new(:host => @host, :port => @port,
|
916
933
|
:encryption => @encryption)
|
917
|
-
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
934
|
+
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
|
918
935
|
@result = conn.modify(args)
|
919
936
|
end
|
920
937
|
ensure
|
921
938
|
conn.close if conn
|
922
939
|
end
|
923
940
|
end
|
924
|
-
|
941
|
+
|
942
|
+
@result.success?
|
925
943
|
end
|
926
944
|
|
927
945
|
# Add a value to an attribute. Takes the full DN of the entry to modify,
|
@@ -985,14 +1003,14 @@ class Net::LDAP
|
|
985
1003
|
begin
|
986
1004
|
conn = Connection.new(:host => @host, :port => @port,
|
987
1005
|
:encryption => @encryption)
|
988
|
-
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
1006
|
+
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
|
989
1007
|
@result = conn.rename(args)
|
990
1008
|
end
|
991
1009
|
ensure
|
992
1010
|
conn.close if conn
|
993
1011
|
end
|
994
1012
|
end
|
995
|
-
@result
|
1013
|
+
@result.success?
|
996
1014
|
end
|
997
1015
|
alias_method :modify_rdn, :rename
|
998
1016
|
|
@@ -1013,16 +1031,29 @@ class Net::LDAP
|
|
1013
1031
|
begin
|
1014
1032
|
conn = Connection.new(:host => @host, :port => @port,
|
1015
1033
|
:encryption => @encryption)
|
1016
|
-
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
1034
|
+
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
|
1017
1035
|
@result = conn.delete(args)
|
1018
1036
|
end
|
1019
1037
|
ensure
|
1020
1038
|
conn.close
|
1021
1039
|
end
|
1022
1040
|
end
|
1023
|
-
@result
|
1041
|
+
@result.success?
|
1024
1042
|
end
|
1025
1043
|
|
1044
|
+
# Delete an entry from the LDAP directory along with all subordinate entries.
|
1045
|
+
# the regular delete method will fail to delete an entry if it has subordinate
|
1046
|
+
# entries. This method sends an extra control code to tell the LDAP server
|
1047
|
+
# to do a tree delete. ('1.2.840.113556.1.4.805')
|
1048
|
+
#
|
1049
|
+
# Returns True or False to indicate whether the delete succeeded. Extended
|
1050
|
+
# status information is available by calling #get_operation_result.
|
1051
|
+
#
|
1052
|
+
# dn = "mail=deleteme@example.com, ou=people, dc=example, dc=com"
|
1053
|
+
# ldap.delete_tree :dn => dn
|
1054
|
+
def delete_tree(args)
|
1055
|
+
delete(args.merge(:control_codes => [[Net::LDAP::LDAPControls::DELETE_TREE, true]]))
|
1056
|
+
end
|
1026
1057
|
# This method is experimental and subject to change. Return the rootDSE
|
1027
1058
|
# record from the LDAP server as a Net::LDAP::Entry, or an empty Entry if
|
1028
1059
|
# the server doesn't return the record.
|
@@ -1092,8 +1123,12 @@ class Net::LDAP
|
|
1092
1123
|
# MUST refactor the root_dse call out.
|
1093
1124
|
#++
|
1094
1125
|
def paged_searches_supported?
|
1126
|
+
# active directory returns that it supports paged results. However
|
1127
|
+
# it returns binary data in the rfc2696_cookie which throws an
|
1128
|
+
# encoding exception breaking searching.
|
1129
|
+
return false if @force_no_page
|
1095
1130
|
@server_caps ||= search_root_dse
|
1096
|
-
@server_caps[:supportedcontrol].include?(Net::LDAP::
|
1131
|
+
@server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
|
1097
1132
|
end
|
1098
1133
|
end # class LDAP
|
1099
1134
|
|
@@ -1237,7 +1272,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
1237
1272
|
|
1238
1273
|
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
|
1239
1274
|
|
1240
|
-
pdu
|
1275
|
+
pdu
|
1241
1276
|
end
|
1242
1277
|
|
1243
1278
|
#--
|
@@ -1275,7 +1310,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
1275
1310
|
@conn.write request_pkt
|
1276
1311
|
|
1277
1312
|
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
|
1278
|
-
return pdu
|
1313
|
+
return pdu unless pdu.result_code == 14 # saslBindInProgress
|
1279
1314
|
raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
|
1280
1315
|
|
1281
1316
|
cred = chall.call(pdu.result_server_sasl_creds)
|
@@ -1315,6 +1350,35 @@ class Net::LDAP::Connection #:nodoc:
|
|
1315
1350
|
end
|
1316
1351
|
private :bind_gss_spnego
|
1317
1352
|
|
1353
|
+
|
1354
|
+
#--
|
1355
|
+
# Allow the caller to specify a sort control
|
1356
|
+
#
|
1357
|
+
# The format of the sort control needs to be:
|
1358
|
+
#
|
1359
|
+
# :sort_control => ["cn"] # just a string
|
1360
|
+
# or
|
1361
|
+
# :sort_control => [["cn", "matchingRule", true]] #attribute, matchingRule, direction (true / false)
|
1362
|
+
# or
|
1363
|
+
# :sort_control => ["givenname","sn"] #multiple strings or arrays
|
1364
|
+
#
|
1365
|
+
def encode_sort_controls(sort_definitions)
|
1366
|
+
return sort_definitions unless sort_definitions
|
1367
|
+
|
1368
|
+
sort_control_values = sort_definitions.map do |control|
|
1369
|
+
control = Array(control) # if there is only an attribute name as a string then infer the orderinrule and reverseorder
|
1370
|
+
control[0] = String(control[0]).to_ber,
|
1371
|
+
control[1] = String(control[1]).to_ber,
|
1372
|
+
control[2] = (control[2] == true).to_ber
|
1373
|
+
control.to_ber_sequence
|
1374
|
+
end
|
1375
|
+
sort_control = [
|
1376
|
+
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
|
1377
|
+
false.to_ber,
|
1378
|
+
sort_control_values.to_ber_sequence.to_s.to_ber
|
1379
|
+
].to_ber_sequence
|
1380
|
+
end
|
1381
|
+
|
1318
1382
|
#--
|
1319
1383
|
# Alternate implementation, this yields each search entry to the caller as
|
1320
1384
|
# it are received.
|
@@ -1340,6 +1404,12 @@ class Net::LDAP::Connection #:nodoc:
|
|
1340
1404
|
scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree
|
1341
1405
|
raise Net::LDAP::LdapError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope)
|
1342
1406
|
|
1407
|
+
sort_control = encode_sort_controls(args.fetch(:sort_controls){ false })
|
1408
|
+
|
1409
|
+
deref = args[:deref] || Net::LDAP::DerefAliases_Never
|
1410
|
+
raise Net::LDAP::LdapError.new( "invalid alias dereferencing value" ) unless Net::LDAP::DerefAliasesArray.include?(deref)
|
1411
|
+
|
1412
|
+
|
1343
1413
|
# An interesting value for the size limit would be close to A/D's
|
1344
1414
|
# built-in page limit of 1000 records, but openLDAP newer than version
|
1345
1415
|
# 2.2.0 chokes on anything bigger than 126. You get a silent error that
|
@@ -1361,7 +1431,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
1361
1431
|
# to do a root-DSE record search and not do a paged search if the LDAP
|
1362
1432
|
# doesn't support it. Yuck.
|
1363
1433
|
rfc2696_cookie = [126, ""]
|
1364
|
-
|
1434
|
+
result_pdu = nil
|
1365
1435
|
n_results = 0
|
1366
1436
|
|
1367
1437
|
loop {
|
@@ -1379,7 +1449,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
1379
1449
|
request = [
|
1380
1450
|
search_base.to_ber,
|
1381
1451
|
scope.to_ber_enumerated,
|
1382
|
-
|
1452
|
+
deref.to_ber_enumerated,
|
1383
1453
|
query_limit.to_ber, # size limit
|
1384
1454
|
0.to_ber,
|
1385
1455
|
attributes_only.to_ber,
|
@@ -1387,20 +1457,25 @@ class Net::LDAP::Connection #:nodoc:
|
|
1387
1457
|
search_attributes.to_ber_sequence
|
1388
1458
|
].to_ber_appsequence(3)
|
1389
1459
|
|
1460
|
+
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
|
1461
|
+
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
|
1462
|
+
# we have to disable paging (even though server supports it) to get around this...
|
1463
|
+
|
1390
1464
|
controls = []
|
1391
1465
|
controls <<
|
1392
1466
|
[
|
1393
|
-
Net::LDAP::
|
1467
|
+
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
|
1394
1468
|
# Criticality MUST be false to interoperate with normal LDAPs.
|
1395
1469
|
false.to_ber,
|
1396
1470
|
rfc2696_cookie.map{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
|
1397
1471
|
].to_ber_sequence if paged_searches_supported
|
1472
|
+
controls << sort_control if sort_control
|
1398
1473
|
controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
|
1399
1474
|
|
1400
1475
|
pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
|
1401
1476
|
@conn.write pkt
|
1402
1477
|
|
1403
|
-
|
1478
|
+
result_pdu = nil
|
1404
1479
|
controls = []
|
1405
1480
|
|
1406
1481
|
while (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be))
|
@@ -1417,9 +1492,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
1417
1492
|
end
|
1418
1493
|
end
|
1419
1494
|
when 5 # search-result
|
1420
|
-
|
1495
|
+
result_pdu = pdu
|
1421
1496
|
controls = pdu.result_controls
|
1422
|
-
if return_referrals && result_code == 10
|
1497
|
+
if return_referrals && pdu.result_code == 10
|
1423
1498
|
if block_given?
|
1424
1499
|
se = Net::LDAP::Entry.new
|
1425
1500
|
se[:search_referrals] = (pdu.search_referrals || [])
|
@@ -1443,9 +1518,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
1443
1518
|
# of type OCTET STRING, covered in the default syntax supported by
|
1444
1519
|
# read_ber, so I guess we're ok.
|
1445
1520
|
more_pages = false
|
1446
|
-
if result_code == 0 and controls
|
1521
|
+
if result_pdu.result_code == 0 and controls
|
1447
1522
|
controls.each do |c|
|
1448
|
-
if c.oid == Net::LDAP::
|
1523
|
+
if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
|
1449
1524
|
# just in case some bogus server sends us more than 1 of these.
|
1450
1525
|
more_pages = false
|
1451
1526
|
if c.value and c.value.length > 0
|
@@ -1462,7 +1537,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
1462
1537
|
break unless more_pages
|
1463
1538
|
} # loop
|
1464
1539
|
|
1465
|
-
result_code
|
1540
|
+
result_pdu || OpenStruct.new(:status => :failure, :result_code => 1, :message => "Invalid search")
|
1466
1541
|
end
|
1467
1542
|
|
1468
1543
|
MODIFY_OPERATIONS = { #:nodoc:
|
@@ -1502,7 +1577,8 @@ class Net::LDAP::Connection #:nodoc:
|
|
1502
1577
|
@conn.write pkt
|
1503
1578
|
|
1504
1579
|
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 7) or raise Net::LDAP::LdapError, "response missing or invalid"
|
1505
|
-
|
1580
|
+
|
1581
|
+
pdu
|
1506
1582
|
end
|
1507
1583
|
|
1508
1584
|
#--
|
@@ -1523,21 +1599,25 @@ class Net::LDAP::Connection #:nodoc:
|
|
1523
1599
|
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
1524
1600
|
@conn.write pkt
|
1525
1601
|
|
1526
|
-
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) &&
|
1527
|
-
|
1602
|
+
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) &&
|
1603
|
+
(pdu = Net::LDAP::PDU.new(be)) &&
|
1604
|
+
(pdu.app_tag == 9) or
|
1605
|
+
raise Net::LDAP::LdapError, "response missing or invalid"
|
1606
|
+
|
1607
|
+
pdu
|
1528
1608
|
end
|
1529
1609
|
|
1530
1610
|
#--
|
1531
1611
|
# TODO: need to support a time limit, in case the server fails to respond.
|
1532
1612
|
#++
|
1533
|
-
def rename
|
1613
|
+
def rename(args)
|
1534
1614
|
old_dn = args[:olddn] or raise "Unable to rename empty DN"
|
1535
1615
|
new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
|
1536
1616
|
delete_attrs = args[:delete_attributes] ? true : false
|
1537
1617
|
new_superior = args[:new_superior]
|
1538
1618
|
|
1539
1619
|
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
|
1540
|
-
request << new_superior.
|
1620
|
+
request << new_superior.to_ber_contextspecific(0) unless new_superior == nil
|
1541
1621
|
|
1542
1622
|
pkt = [next_msgid.to_ber, request.to_ber_appsequence(12)].to_ber_sequence
|
1543
1623
|
@conn.write pkt
|
@@ -1545,7 +1625,8 @@ class Net::LDAP::Connection #:nodoc:
|
|
1545
1625
|
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) &&
|
1546
1626
|
(pdu = Net::LDAP::PDU.new( be )) && (pdu.app_tag == 13) or
|
1547
1627
|
raise Net::LDAP::LdapError.new( "response missing or invalid" )
|
1548
|
-
|
1628
|
+
|
1629
|
+
pdu
|
1549
1630
|
end
|
1550
1631
|
|
1551
1632
|
#--
|
@@ -1553,12 +1634,13 @@ class Net::LDAP::Connection #:nodoc:
|
|
1553
1634
|
#++
|
1554
1635
|
def delete(args)
|
1555
1636
|
dn = args[:dn] or raise "Unable to delete empty DN"
|
1556
|
-
|
1637
|
+
controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
|
1557
1638
|
request = dn.to_s.to_ber_application_string(10)
|
1558
|
-
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
1639
|
+
pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
|
1559
1640
|
@conn.write pkt
|
1560
1641
|
|
1561
1642
|
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 11) or raise Net::LDAP::LdapError, "response missing or invalid"
|
1562
|
-
|
1643
|
+
|
1644
|
+
pdu
|
1563
1645
|
end
|
1564
1646
|
end # class Connection
|