net-ldap 0.3.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

@@ -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
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ script: bundle exec rake spec
@@ -19,3 +19,4 @@ Contributions since:
19
19
  * Derek Harmel (derekharmel)
20
20
  * Erik Hetzner (egh)
21
21
  * nowhereman
22
+ * David J. Lee (DavidJLee)
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -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
@@ -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
@@ -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.8.7 interpreter or better.
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.url = %W(http://rubyldap.com/ https://github.com/ruby-ldap/ruby-net-ldap)
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", "~> 0.9.0" ]
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"
@@ -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 = '0.3.1'
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
- force_encoding('UTF-8') if respond_to?(:encoding)
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
- self.encode('UTF-8').force_encoding('ASCII-8BIT')
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
@@ -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 LdapControls
339
- PagedResults = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
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 @result.is_a?(Hash)
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 = (@result[:resultCode] || "").to_i
524
- os.error_message = @result[:errorMessage]
525
- os.matched_dn = @result[:matchedDN]
526
- elsif @result
527
- os.code = @result
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 == 0
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 == 0
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 == 0
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
- @result == 0
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 == 0
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 == 0
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::LdapControls::PagedResults)
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.result_code
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.result_code unless pdu.result_code == 14 # saslBindInProgress
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
- result_code = 0
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
- 0.to_ber_enumerated,
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::LdapControls::PagedResults.to_ber,
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
- result_code = 0
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
- result_code = pdu.result_code
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::LdapControls::PagedResults
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
- pdu.result_code
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)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 9) or raise Net::LDAP::LdapError, "response missing or invalid"
1527
- pdu.result_code
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 args
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.to_ber unless new_superior == nil
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
- pdu.result_code
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
- pdu.result_code
1643
+
1644
+ pdu
1563
1645
  end
1564
1646
  end # class Connection