net-ldap 0.8.0 → 0.9.0

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.

Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -0
  3. data/.travis.yml +19 -1
  4. data/CONTRIBUTING.md +54 -0
  5. data/Hacking.rdoc +2 -4
  6. data/History.rdoc +37 -0
  7. data/Manifest.txt +0 -4
  8. data/README.rdoc +8 -0
  9. data/Rakefile +1 -3
  10. data/lib/net/ber/core_ext.rb +5 -5
  11. data/lib/net/ber/core_ext/string.rb +7 -7
  12. data/lib/net/ber/core_ext/true_class.rb +2 -3
  13. data/lib/net/ldap.rb +134 -620
  14. data/lib/net/ldap/connection.rb +692 -0
  15. data/lib/net/ldap/dataset.rb +18 -4
  16. data/lib/net/ldap/entry.rb +1 -1
  17. data/lib/net/ldap/filter.rb +7 -7
  18. data/lib/net/ldap/password.rb +11 -11
  19. data/lib/net/ldap/pdu.rb +28 -4
  20. data/lib/net/ldap/version.rb +1 -1
  21. data/lib/net/snmp.rb +235 -241
  22. data/net-ldap.gemspec +7 -33
  23. data/script/install-openldap +47 -0
  24. data/script/package +7 -0
  25. data/script/release +16 -0
  26. data/test/ber/core_ext/test_array.rb +22 -0
  27. data/test/ber/core_ext/test_string.rb +25 -0
  28. data/test/ber/test_ber.rb +126 -0
  29. data/test/fixtures/openldap/memberof.ldif +33 -0
  30. data/test/fixtures/openldap/retcode.ldif +76 -0
  31. data/test/fixtures/openldap/slapd.conf.ldif +67 -0
  32. data/test/fixtures/seed.ldif +374 -0
  33. data/test/integration/test_add.rb +28 -0
  34. data/test/integration/test_ber.rb +30 -0
  35. data/test/integration/test_bind.rb +22 -0
  36. data/test/integration/test_delete.rb +31 -0
  37. data/test/integration/test_open.rb +88 -0
  38. data/test/integration/test_return_codes.rb +38 -0
  39. data/test/integration/test_search.rb +77 -0
  40. data/test/support/vm/openldap/.gitignore +1 -0
  41. data/test/support/vm/openldap/README.md +32 -0
  42. data/test/support/vm/openldap/Vagrantfile +33 -0
  43. data/test/test_dn.rb +44 -0
  44. data/test/test_entry.rb +62 -56
  45. data/test/test_filter.rb +98 -2
  46. data/test/test_filter_parser.rb +16 -0
  47. data/test/test_helper.rb +54 -0
  48. data/test/test_ldap.rb +60 -0
  49. data/test/test_ldap_connection.rb +382 -2
  50. data/test/test_ldif.rb +26 -1
  51. data/test/test_password.rb +3 -10
  52. data/test/test_rename.rb +2 -2
  53. data/test/test_search.rb +39 -0
  54. data/test/test_snmp.rb +1 -1
  55. data/test/test_ssl_ber.rb +40 -0
  56. metadata +70 -75
  57. data/.autotest +0 -11
  58. data/.gemtest +0 -0
  59. data/.rspec +0 -2
  60. data/autotest/discover.rb +0 -1
  61. data/spec/integration/ssl_ber_spec.rb +0 -39
  62. data/spec/spec.opts +0 -2
  63. data/spec/spec_helper.rb +0 -28
  64. data/spec/unit/ber/ber_spec.rb +0 -141
  65. data/spec/unit/ber/core_ext/array_spec.rb +0 -24
  66. data/spec/unit/ber/core_ext/string_spec.rb +0 -51
  67. data/spec/unit/ldap/dn_spec.rb +0 -80
  68. data/spec/unit/ldap/entry_spec.rb +0 -51
  69. data/spec/unit/ldap/filter_parser_spec.rb +0 -26
  70. data/spec/unit/ldap/filter_spec.rb +0 -115
  71. data/spec/unit/ldap/search_spec.rb +0 -49
  72. data/spec/unit/ldap_spec.rb +0 -223
  73. data/test/common.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 487807bc08da7baeb4941090020468a8e60fe14a
4
- data.tar.gz: 047ccb6638a81c96eb1bfca1a684726a71bfc1a8
3
+ metadata.gz: f9fc3d7a463a0f673de926b440e144eae66937a4
4
+ data.tar.gz: 2d921f1be37ff33c20100650642f1123fcd2c161
5
5
  SHA512:
6
- metadata.gz: 6d02d5a7af488a5f7ee8b8050c5d281d6e76b7c40baeb1a1275a18b103d2f76cf0fea8dfa865efe2ce243d92b87c3d6abdfa1b47615cf95fb5cd5e9b9806e70a
7
- data.tar.gz: 380cc5961a9a85a1d196203b6712061b5c8b8a4c1008dcfcee67556029269e264d2d7463e2e98572c06020dbae44dbb8ace34f06d584d33a9f1b5f6169f90d9d
6
+ metadata.gz: 803d644ca0fe9b7314df587e68a0ecc7f01f34316d30fd4a926a09dfc4e3ab7e73b388854f9f4eecc78c9b07817d21a0b342e893422d21b8a682c35dcf4a8fb1
7
+ data.tar.gz: e5143361c56f7c60cfe7272c054860403ec53812660730e141688be9fb56adf32b0121ae1d5e27c4a076a963b07b509db96e7a8cc43fa0eec7d5cf0b1e92dcdd
@@ -0,0 +1,7 @@
1
+ *~
2
+ *.swp
3
+ .rvmrc
4
+ pkg/
5
+ doc/
6
+ publish/
7
+ Gemfile.lock
@@ -2,9 +2,27 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.2
6
+ # optional
5
7
  - jruby-19mode
6
8
  - rbx-19mode
9
+ - rbx-2
10
+
11
+ env:
12
+ - INTEGRATION=openldap
13
+
14
+ install:
15
+ - if [ "$INTEGRATION" = "openldap" ]; then ./script/install-openldap; fi
16
+ - bundle install
17
+
18
+ script: bundle exec rake
19
+
7
20
  matrix:
8
21
  allow_failures:
9
22
  - rvm: jruby-19mode
10
- script: bundle exec rake spec
23
+ - rvm: rbx-19mode
24
+ - rvm: rbx-2
25
+ fast_finish: true
26
+
27
+ notifications:
28
+ email: false
@@ -0,0 +1,54 @@
1
+ # Contribution guide
2
+
3
+ Thank you for using net-ldap. If you'd like to help, keep these guidelines in
4
+ mind.
5
+
6
+ ## Submitting a New Issue
7
+
8
+ If you find a bug, or would like to propose an idea, file a [new issue][issues].
9
+ Include as many details as possible:
10
+
11
+ - Version of net-ldap gem
12
+ - LDAP server version
13
+ - Queries, connection information, any other input
14
+ - output or error messages
15
+
16
+ ## Sending a Pull Request
17
+
18
+ [Pull requests][pr] are always welcome!
19
+
20
+ Check out [the project's issues list][issues] for ideas on what could be improved.
21
+
22
+ Before sending, please add tests and ensure the test suite passes.
23
+
24
+ To run the full suite:
25
+
26
+ `bundle exec rake`
27
+
28
+ To run a specific test file:
29
+
30
+ `bundle exec ruby test/test_ldap.rb`
31
+
32
+ To run a specific test:
33
+
34
+ `bundle exec ruby test/test_ldap.rb -n test_instrument_bind`
35
+
36
+ Pull requests will trigger automatic continuous integration builds on
37
+ [TravisCI][travis]. To run integration tests locally, see the `test/support`
38
+ folder.
39
+
40
+ ## Styleguide
41
+
42
+ ```ruby
43
+ # 1.9+ style hashes
44
+ {key: "value"}
45
+
46
+ # Multi-line arguments with `\`
47
+ MyClass.new \
48
+ foo: 'bar',
49
+ baz: 'garply'
50
+ ```
51
+
52
+ [issues]: https://github.com/ruby-net-ldap/ruby-net-ldap/issues
53
+ [pr]: https://help.github.com/articles/using-pull-requests
54
+ [travis]: https://travis-ci.org/ruby-ldap/ruby-net-ldap
@@ -40,8 +40,8 @@ modification to +Contributors.rdoc+ to add yourself.
40
40
 
41
41
  == Tests
42
42
 
43
- The Net::LDAP team uses RSpec for unit testing; all changes must have rspec
44
- tests for any new or changed features.
43
+ The Net::LDAP team uses [Minitest](http://docs.seattlerb.org/minitest/) for unit
44
+ testing; all changes must have tests for any new or changed features.
45
45
 
46
46
  Your changes should have been tested against at least one real LDAP server; the
47
47
  current tests are not sufficient to find all possible bugs. It's unlikely that
@@ -57,8 +57,6 @@ installed using RubyGems.
57
57
 
58
58
  * *hoe*
59
59
  * *hoe-git*
60
- * *metaid*
61
- * *rspec*
62
60
  * *flexmock*
63
61
 
64
62
  == Participation
@@ -1,3 +1,40 @@
1
+ === Net::LDAP 0.9.0
2
+ * Major changes:
3
+ * Dropped support for ruby 1.8.7, ruby >= 1.9.3 now required
4
+ * Major enhancements:
5
+ * Add support for search time limit parameter
6
+ * Instrument received messages, PDU parsing
7
+ * Minor enhancments:
8
+ * Add support for querying ActiveDirectory capabilities from root dse
9
+ * Bug fixes:
10
+ * Fix reads for multiple concurrent requests with shared, open connections mixing up the results
11
+ * Fix search size option
12
+ * Fix BER encoding bug
13
+ * Code clean-up:
14
+ * Added integration test suite
15
+ * Switch to minitest
16
+
17
+ * Details
18
+ * #150 Support querying ActiveDirectory capabilities when searching root dse
19
+ * #142 Encode true as xFF
20
+ * #124, #145, #146, #152 Cleanup gemspec
21
+ * #138, #144 Track response messages by message id
22
+ * #141 Magic number/constant cleanup
23
+ * #119, #129, #130, #132, #133, #137 Integration tests
24
+ * #115 Search timeout support
25
+ * #140 Fix search size option
26
+ * #139 Cleanup and inline documentation for Net::LDAP::Connection#search
27
+ * #131 Instrumentation
28
+ * #116 Refactor Connection#write
29
+ * #126 Update gitignore
30
+ * #128 Fix whitespace
31
+ * #113, #121 Switch to minitest
32
+ * #123 Base64 encoded dn
33
+ * #114 Separate file for Net::LDAP::Connection
34
+ * #104 Parse version spec in LDIF datasets
35
+ * #106 ldap.modify doc fixes
36
+ * #111 Fix test deprecations
37
+
1
38
  === Net::LDAP 0.5.0 / 2013-07-22
2
39
  * Major changes:
3
40
  * Required Ruby version is >=1.9.3
@@ -1,5 +1,3 @@
1
- .autotest
2
- .rspec
3
1
  .travis.yml
4
2
  Contributors.rdoc
5
3
  Gemfile
@@ -9,7 +7,6 @@ License.rdoc
9
7
  Manifest.txt
10
8
  README.rdoc
11
9
  Rakefile
12
- autotest/discover.rb
13
10
  lib/net-ldap.rb
14
11
  lib/net/ber.rb
15
12
  lib/net/ber/ber_parser.rb
@@ -32,7 +29,6 @@ lib/net/ldap/version.rb
32
29
  lib/net/snmp.rb
33
30
  net-ldap.gemspec
34
31
  spec/integration/ssl_ber_spec.rb
35
- spec/spec.opts
36
32
  spec/spec_helper.rb
37
33
  spec/unit/ber/ber_spec.rb
38
34
  spec/unit/ber/core_ext/array_spec.rb
@@ -37,6 +37,14 @@ sources.
37
37
 
38
38
  Simply require either 'net-ldap' or 'net/ldap'.
39
39
 
40
+ == Release
41
+
42
+ This section is for gem maintainers to cut a new version of the gem.
43
+
44
+ * Update lib/html/pipeline/version.rb to next version number X.X.X following {semver}(http://semver.org/).
45
+ * Update CHANGELOG.md. Get latest changes with `git log --oneline vLAST_RELEASE..HEAD | grep Merge`
46
+ * On the master branch, run `script/release`
47
+
40
48
  :include: Contributors.rdoc
41
49
 
42
50
  :include: License.rdoc
data/Rakefile CHANGED
@@ -30,9 +30,7 @@ Hoe.spec 'net-ldap' do |spec|
30
30
 
31
31
  spec.extra_dev_deps << [ "hoe-git", "~> 1" ]
32
32
  spec.extra_dev_deps << [ "hoe-gemspec", "~> 1" ]
33
- spec.extra_dev_deps << [ "metaid", "~> 1" ]
34
33
  spec.extra_dev_deps << [ "flexmock", ">= 1.3.0" ]
35
- spec.extra_dev_deps << [ "rspec", "~> 2.0" ]
36
34
 
37
35
  spec.clean_globs << "coverage"
38
36
 
@@ -70,7 +68,7 @@ namespace :old do
70
68
  end
71
69
  end
72
70
 
73
- desc "Run a full set of integration and unit tests"
71
+ desc "Run a full set of integration and unit tests"
74
72
  task :cruise => [:test, :spec]
75
73
 
76
74
  # vim: syntax=ruby
@@ -28,35 +28,35 @@ end
28
28
 
29
29
  require 'net/ber/core_ext/array'
30
30
  # :stopdoc:
31
- class Array
31
+ class Array
32
32
  include Net::BER::Extensions::Array
33
33
  end
34
34
  # :startdoc:
35
35
 
36
36
  require 'net/ber/core_ext/bignum'
37
37
  # :stopdoc:
38
- class Bignum
38
+ class Bignum
39
39
  include Net::BER::Extensions::Bignum
40
40
  end
41
41
  # :startdoc:
42
42
 
43
43
  require 'net/ber/core_ext/fixnum'
44
44
  # :stopdoc:
45
- class Fixnum
45
+ class Fixnum
46
46
  include Net::BER::Extensions::Fixnum
47
47
  end
48
48
  # :startdoc:
49
49
 
50
50
  require 'net/ber/core_ext/true_class'
51
51
  # :stopdoc:
52
- class TrueClass
52
+ class TrueClass
53
53
  include Net::BER::Extensions::TrueClass
54
54
  end
55
55
  # :startdoc:
56
56
 
57
57
  require 'net/ber/core_ext/false_class'
58
58
  # :stopdoc:
59
- class FalseClass
59
+ class FalseClass
60
60
  include Net::BER::Extensions::FalseClass
61
61
  end
62
62
  # :startdoc:
@@ -16,13 +16,13 @@ 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
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
26
 
27
27
  def raw_utf8_encoded
28
28
  if self.respond_to?(:encode)
@@ -5,8 +5,7 @@ module Net::BER::Extensions::TrueClass
5
5
  ##
6
6
  # Converts +true+ to the BER wireline representation of +true+.
7
7
  def to_ber
8
- # 20100319 AZ: Note that this may not be the completely correct value,
9
- # per some test documentation. We need to determine the truth of this.
10
- "\001\001\001"
8
+ # http://tools.ietf.org/html/rfc4511#section-5.1
9
+ "\001\001\xFF".force_encoding("ASCII-8BIT")
11
10
  end
12
11
  end
@@ -24,6 +24,7 @@ require 'net/ldap/dataset'
24
24
  require 'net/ldap/password'
25
25
  require 'net/ldap/entry'
26
26
  require 'net/ldap/instrumentation'
27
+ require 'net/ldap/connection'
27
28
  require 'net/ldap/version'
28
29
 
29
30
  # == Quick-start for the Impatient
@@ -316,33 +317,107 @@ class Net::LDAP
316
317
  DefaultPort = 389
317
318
  DefaultAuth = { :method => :anonymous }
318
319
  DefaultTreebase = "dc=com"
319
- DefaultForceNoPage = false
320
+ DefaultForceNoPage = false
320
321
 
321
322
  StartTlsOid = "1.3.6.1.4.1.1466.20037"
322
323
 
324
+ # https://tools.ietf.org/html/rfc4511#section-4.1.9
325
+ # https://tools.ietf.org/html/rfc4511#appendix-A
326
+ ResultCodeSuccess = 0
327
+ ResultCodeOperationsError = 1
328
+ ResultCodeProtocolError = 2
329
+ ResultCodeTimeLimitExceeded = 3
330
+ ResultCodeSizeLimitExceeded = 4
331
+ ResultCodeCompareFalse = 5
332
+ ResultCodeCompareTrue = 6
333
+ ResultCodeAuthMethodNotSupported = 7
334
+ ResultCodeStrongerAuthRequired = 8
335
+ ResultCodeReferral = 10
336
+ ResultCodeAdminLimitExceeded = 11
337
+ ResultCodeUnavailableCriticalExtension = 12
338
+ ResultCodeConfidentialityRequired = 13
339
+ ResultCodeSaslBindInProgress = 14
340
+ ResultCodeNoSuchAttribute = 16
341
+ ResultCodeUndefinedAttributeType = 17
342
+ ResultCodeInappropriateMatching = 18
343
+ ResultCodeConstraintViolation = 19
344
+ ResultCodeAttributeOrValueExists = 20
345
+ ResultCodeInvalidAttributeSyntax = 21
346
+ ResultCodeNoSuchObject = 32
347
+ ResultCodeAliasProblem = 33
348
+ ResultCodeInvalidDNSyntax = 34
349
+ ResultCodeAliasDereferencingProblem = 36
350
+ ResultCodeInappropriateAuthentication = 48
351
+ ResultCodeInvalidCredentials = 49
352
+ ResultCodeInsufficientAccessRights = 50
353
+ ResultCodeBusy = 51
354
+ ResultCodeUnavailable = 52
355
+ ResultCodeUnwillingToPerform = 53
356
+ ResultCodeNamingViolation = 64
357
+ ResultCodeObjectClassViolation = 65
358
+ ResultCodeNotAllowedOnNonLeaf = 66
359
+ ResultCodeNotAllowedOnRDN = 67
360
+ ResultCodeEntryAlreadyExists = 68
361
+ ResultCodeObjectClassModsProhibited = 69
362
+ ResultCodeAffectsMultipleDSAs = 71
363
+ ResultCodeOther = 80
364
+
365
+ # https://tools.ietf.org/html/rfc4511#appendix-A.1
366
+ ResultCodesNonError = [
367
+ ResultCodeSuccess,
368
+ ResultCodeCompareFalse,
369
+ ResultCodeCompareTrue,
370
+ ResultCodeReferral,
371
+ ResultCodeSaslBindInProgress
372
+ ]
373
+
374
+ # nonstandard list of "successful" result codes for searches
375
+ ResultCodesSearchSuccess = [
376
+ ResultCodeSuccess,
377
+ ResultCodeTimeLimitExceeded,
378
+ ResultCodeSizeLimitExceeded
379
+ ]
380
+
381
+ # map of result code to human message
323
382
  ResultStrings = {
324
- 0 => "Success",
325
- 1 => "Operations Error",
326
- 2 => "Protocol Error",
327
- 3 => "Time Limit Exceeded",
328
- 4 => "Size Limit Exceeded",
329
- 10 => "Referral",
330
- 12 => "Unavailable crtical extension",
331
- 14 => "saslBindInProgress",
332
- 16 => "No Such Attribute",
333
- 17 => "Undefined Attribute Type",
334
- 19 => "Constraint Violation",
335
- 20 => "Attribute or Value Exists",
336
- 32 => "No Such Object",
337
- 34 => "Invalid DN Syntax",
338
- 48 => "Inappropriate Authentication",
339
- 49 => "Invalid Credentials",
340
- 50 => "Insufficient Access Rights",
341
- 51 => "Busy",
342
- 52 => "Unavailable",
343
- 53 => "Unwilling to perform",
344
- 65 => "Object Class Violation",
345
- 68 => "Entry Already Exists"
383
+ ResultCodeSuccess => "Success",
384
+ ResultCodeOperationsError => "Operations Error",
385
+ ResultCodeProtocolError => "Protocol Error",
386
+ ResultCodeTimeLimitExceeded => "Time Limit Exceeded",
387
+ ResultCodeSizeLimitExceeded => "Size Limit Exceeded",
388
+ ResultCodeCompareFalse => "False Comparison",
389
+ ResultCodeCompareTrue => "True Comparison",
390
+ ResultCodeAuthMethodNotSupported => "Auth Method Not Supported",
391
+ ResultCodeStrongerAuthRequired => "Stronger Auth Needed",
392
+ ResultCodeReferral => "Referral",
393
+ ResultCodeAdminLimitExceeded => "Admin Limit Exceeded",
394
+ ResultCodeUnavailableCriticalExtension => "Unavailable crtical extension",
395
+ ResultCodeConfidentialityRequired => "Confidentiality Required",
396
+ ResultCodeSaslBindInProgress => "saslBindInProgress",
397
+ ResultCodeNoSuchAttribute => "No Such Attribute",
398
+ ResultCodeUndefinedAttributeType => "Undefined Attribute Type",
399
+ ResultCodeInappropriateMatching => "Inappropriate Matching",
400
+ ResultCodeConstraintViolation => "Constraint Violation",
401
+ ResultCodeAttributeOrValueExists => "Attribute or Value Exists",
402
+ ResultCodeInvalidAttributeSyntax => "Invalide Attribute Syntax",
403
+ ResultCodeNoSuchObject => "No Such Object",
404
+ ResultCodeAliasProblem => "Alias Problem",
405
+ ResultCodeInvalidDNSyntax => "Invalid DN Syntax",
406
+ ResultCodeAliasDereferencingProblem => "Alias Dereferencing Problem",
407
+ ResultCodeInappropriateAuthentication => "Inappropriate Authentication",
408
+ ResultCodeInvalidCredentials => "Invalid Credentials",
409
+ ResultCodeInsufficientAccessRights => "Insufficient Access Rights",
410
+ ResultCodeBusy => "Busy",
411
+ ResultCodeUnavailable => "Unavailable",
412
+ ResultCodeUnwillingToPerform => "Unwilling to perform",
413
+ ResultCodeNamingViolation => "Naming Violation",
414
+ ResultCodeObjectClassViolation => "Object Class Violation",
415
+ ResultCodeNotAllowedOnNonLeaf => "Not Allowed On Non-Leaf",
416
+ ResultCodeNotAllowedOnRDN => "Not Allowed On RDN",
417
+ ResultCodeEntryAlreadyExists => "Entry Already Exists",
418
+ ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
419
+ ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
420
+ ResultCodeOther => "Other"
346
421
  }
347
422
 
348
423
  module LDAPControls
@@ -397,7 +472,7 @@ class Net::LDAP
397
472
  @verbose = false # Make this configurable with a switch on the class.
398
473
  @auth = args[:auth] || DefaultAuth
399
474
  @base = args[:base] || DefaultTreebase
400
- @force_no_page = args[:force_no_page] || DefaultForceNoPage
475
+ @force_no_page = args[:force_no_page] || DefaultForceNoPage
401
476
  encryption args[:encryption] # may be nil
402
477
 
403
478
  if pr = @auth[:password] and pr.respond_to?(:call)
@@ -485,9 +560,9 @@ class Net::LDAP
485
560
  # standard port for simple-TLS encrypted connections is 636. Be sure you
486
561
  # are using the correct port.
487
562
  #
488
- # <i>[Note: a future version of Net::LDAP will support the STARTTLS LDAP
489
- # control, which will enable encrypted communications on the same TCP port
490
- # used for unencrypted connections.]</i>
563
+ # The :start_tls like the :simple_tls encryption method also encrypts all
564
+ # communcations with the LDAP server. With the exception that it operates
565
+ # over the standard TCP port.
491
566
  def encryption(args)
492
567
  case args
493
568
  when :simple_tls, :start_tls
@@ -548,7 +623,7 @@ class Net::LDAP
548
623
  elsif result
549
624
  os.code = result
550
625
  else
551
- os.code = 0
626
+ os.code = Net::LDAP::ResultCodeSuccess
552
627
  end
553
628
  os.message = Net::LDAP.result2string(os.code)
554
629
  os
@@ -609,6 +684,7 @@ class Net::LDAP
609
684
  # Net::LDAP::SearchScope_WholeSubtree. Default is WholeSubtree.)
610
685
  # * :size (an integer indicating the maximum number of search entries to
611
686
  # return. Default is zero, which signifies no limit.)
687
+ # * :time (an integer restricting the maximum time in seconds allowed for a search. Default is zero, no time limit RFC 4511 4.5.1.5)
612
688
  # * :deref (one of: Net::LDAP::DerefAliases_Never, Net::LDAP::DerefAliases_Search,
613
689
  # Net::LDAP::DerefAliases_Find, Net::LDAP::DerefAliases_Always. Default is Never.)
614
690
  #
@@ -665,7 +741,7 @@ class Net::LDAP
665
741
  :port => @port,
666
742
  :encryption => @encryption,
667
743
  :instrumentation_service => @instrumentation_service
668
- if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
744
+ if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
669
745
  @result = conn.search(args) { |entry|
670
746
  result_set << entry if result_set
671
747
  yield entry if block_given?
@@ -677,7 +753,11 @@ class Net::LDAP
677
753
  end
678
754
 
679
755
  if return_result_set
680
- (!@result.nil? && @result.result_code == 0) ? result_set : nil
756
+ unless @result.nil?
757
+ if ResultCodesSearchSuccess.include?(@result.result_code)
758
+ result_set
759
+ end
760
+ end
681
761
  else
682
762
  @result.success?
683
763
  end
@@ -860,7 +940,7 @@ class Net::LDAP
860
940
  :port => @port,
861
941
  :encryption => @encryption,
862
942
  :instrumentation_service => @instrumentation_service
863
- if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
943
+ if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
864
944
  @result = conn.add(args)
865
945
  end
866
946
  ensure
@@ -894,9 +974,10 @@ class Net::LDAP
894
974
  # operations in order.
895
975
  #
896
976
  # Each of the operations appearing in the Array must itself be an Array
897
- # with exactly three elements: an operator:: must be :add, :replace, or
898
- # :delete an attribute name:: the attribute name (string or symbol) to
899
- # modify a value:: either a string or an array of strings.
977
+ # with exactly three elements:
978
+ # an operator :: must be :add, :replace, or :delete
979
+ # an attribute name :: the attribute name (string or symbol) to modify
980
+ # a value :: either a string or an array of strings.
900
981
  #
901
982
  # The :add operator will, unsurprisingly, add the specified values to the
902
983
  # specified attribute. If the attribute does not already exist, :add will
@@ -939,13 +1020,13 @@ class Net::LDAP
939
1020
  # may not get extended information that will tell you which one failed.
940
1021
  # #modify has no notion of an atomic transaction. If you specify a chain
941
1022
  # of modifications in one call to #modify, and one of them fails, the
942
- # preceding ones will usually not be "rolled back, " resulting in a
1023
+ # preceding ones will usually not be "rolled back", resulting in a
943
1024
  # partial update. This is a limitation of the LDAP protocol, not of
944
1025
  # Net::LDAP.
945
1026
  #
946
1027
  # The lack of transactional atomicity in LDAP means that you're usually
947
1028
  # better off using the convenience methods #add_attribute,
948
- # #replace_attribute, and #delete_attribute, which are are wrappers over
1029
+ # #replace_attribute, and #delete_attribute, which are wrappers over
949
1030
  # #modify. However, certain LDAP servers may provide concurrency
950
1031
  # semantics, in which the several operations contained in a single #modify
951
1032
  # call are not interleaved with other modification-requests received
@@ -963,7 +1044,7 @@ class Net::LDAP
963
1044
  :port => @port,
964
1045
  :encryption => @encryption,
965
1046
  :instrumentation_service => @instrumentation_service
966
- if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1047
+ if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
967
1048
  @result = conn.modify(args)
968
1049
  end
969
1050
  ensure
@@ -1040,7 +1121,7 @@ class Net::LDAP
1040
1121
  :port => @port,
1041
1122
  :encryption => @encryption,
1042
1123
  :instrumentation_service => @instrumentation_service
1043
- if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1124
+ if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
1044
1125
  @result = conn.rename(args)
1045
1126
  end
1046
1127
  ensure
@@ -1073,7 +1154,7 @@ class Net::LDAP
1073
1154
  :port => @port,
1074
1155
  :encryption => @encryption,
1075
1156
  :instrumentation_service => @instrumentation_service
1076
- if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
1157
+ if (@result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
1077
1158
  @result = conn.delete(args)
1078
1159
  end
1079
1160
  ensure
@@ -1113,9 +1194,16 @@ class Net::LDAP
1113
1194
  def search_root_dse
1114
1195
  rs = search(:ignore_server_caps => true, :base => "",
1115
1196
  :scope => SearchScope_BaseObject,
1116
- :attributes => [ :namingContexts, :supportedLdapVersion,
1117
- :altServer, :supportedControl, :supportedExtension,
1118
- :supportedFeatures, :supportedSASLMechanisms])
1197
+ :attributes => [
1198
+ :altServer,
1199
+ :namingContexts,
1200
+ :supportedCapabilities,
1201
+ :supportedControl,
1202
+ :supportedExtension,
1203
+ :supportedFeatures,
1204
+ :supportedLdapVersion,
1205
+ :supportedSASLMechanisms
1206
+ ])
1119
1207
  (rs and rs.first) or Net::LDAP::Entry.new
1120
1208
  end
1121
1209
 
@@ -1166,585 +1254,11 @@ class Net::LDAP
1166
1254
  # MUST refactor the root_dse call out.
1167
1255
  #++
1168
1256
  def paged_searches_supported?
1169
- # active directory returns that it supports paged results. However
1170
- # it returns binary data in the rfc2696_cookie which throws an
1171
- # encoding exception breaking searching.
1172
- return false if @force_no_page
1257
+ # active directory returns that it supports paged results. However
1258
+ # it returns binary data in the rfc2696_cookie which throws an
1259
+ # encoding exception breaking searching.
1260
+ return false if @force_no_page
1173
1261
  @server_caps ||= search_root_dse
1174
1262
  @server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
1175
1263
  end
1176
1264
  end # class LDAP
1177
-
1178
- # This is a private class used internally by the library. It should not
1179
- # be called by user code.
1180
- class Net::LDAP::Connection #:nodoc:
1181
- include Net::LDAP::Instrumentation
1182
-
1183
- LdapVersion = 3
1184
- MaxSaslChallenges = 10
1185
-
1186
- def initialize(server)
1187
- @instrumentation_service = server[:instrumentation_service]
1188
-
1189
- begin
1190
- @conn = TCPSocket.new(server[:host], server[:port])
1191
- rescue SocketError
1192
- raise Net::LDAP::LdapError, "No such address or other socket error."
1193
- rescue Errno::ECONNREFUSED
1194
- raise Net::LDAP::LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}."
1195
- end
1196
-
1197
- if server[:encryption]
1198
- setup_encryption server[:encryption]
1199
- end
1200
-
1201
- yield self if block_given?
1202
- end
1203
-
1204
- module GetbyteForSSLSocket
1205
- def getbyte
1206
- getc.ord
1207
- end
1208
- end
1209
-
1210
- module FixSSLSocketSyncClose
1211
- def close
1212
- super
1213
- io.close
1214
- end
1215
- end
1216
-
1217
- def self.wrap_with_ssl(io)
1218
- raise Net::LDAP::LdapError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
1219
- ctx = OpenSSL::SSL::SSLContext.new
1220
- conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
1221
- conn.connect
1222
-
1223
- # Doesn't work:
1224
- # conn.sync_close = true
1225
-
1226
- conn.extend(GetbyteForSSLSocket) unless conn.respond_to?(:getbyte)
1227
- conn.extend(FixSSLSocketSyncClose)
1228
-
1229
- conn
1230
- end
1231
-
1232
- #--
1233
- # Helper method called only from new, and only after we have a
1234
- # successfully-opened @conn instance variable, which is a TCP connection.
1235
- # Depending on the received arguments, we establish SSL, potentially
1236
- # replacing the value of @conn accordingly. Don't generate any errors here
1237
- # if no encryption is requested. DO raise Net::LDAP::LdapError objects if encryption
1238
- # is requested and we have trouble setting it up. That includes if OpenSSL
1239
- # is not set up on the machine. (Question: how does the Ruby OpenSSL
1240
- # wrapper react in that case?) DO NOT filter exceptions raised by the
1241
- # OpenSSL library. Let them pass back to the user. That should make it
1242
- # easier for us to debug the problem reports. Presumably (hopefully?) that
1243
- # will also produce recognizable errors if someone tries to use this on a
1244
- # machine without OpenSSL.
1245
- #
1246
- # The simple_tls method is intended as the simplest, stupidest, easiest
1247
- # solution for people who want nothing more than encrypted comms with the
1248
- # LDAP server. It doesn't do any server-cert validation and requires
1249
- # nothing in the way of key files and root-cert files, etc etc. OBSERVE:
1250
- # WE REPLACE the value of @conn, which is presumed to be a connected
1251
- # TCPSocket object.
1252
- #
1253
- # The start_tls method is supported by many servers over the standard LDAP
1254
- # port. It does not require an alternative port for encrypted
1255
- # communications, as with simple_tls. Thanks for Kouhei Sutou for
1256
- # generously contributing the :start_tls path.
1257
- #++
1258
- def setup_encryption(args)
1259
- case args[:method]
1260
- when :simple_tls
1261
- @conn = self.class.wrap_with_ssl(@conn)
1262
- # additional branches requiring server validation and peer certs, etc.
1263
- # go here.
1264
- when :start_tls
1265
- msgid = next_msgid.to_ber
1266
- request = [Net::LDAP::StartTlsOid.to_ber_contextspecific(0)].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
1267
- request_pkt = [msgid, request].to_ber_sequence
1268
- write request_pkt
1269
- be = read
1270
- raise Net::LDAP::LdapError, "no start_tls result" if be.nil?
1271
- pdu = Net::LDAP::PDU.new(be)
1272
- raise Net::LDAP::LdapError, "no start_tls result" if pdu.nil?
1273
- if pdu.result_code.zero?
1274
- @conn = self.class.wrap_with_ssl(@conn)
1275
- else
1276
- raise Net::LDAP::LdapError, "start_tls failed: #{pdu.result_code}"
1277
- end
1278
- else
1279
- raise Net::LDAP::LdapError, "unsupported encryption method #{args[:method]}"
1280
- end
1281
- end
1282
-
1283
- #--
1284
- # This is provided as a convenience method to make sure a connection
1285
- # object gets closed without waiting for a GC to happen. Clients shouldn't
1286
- # have to call it, but perhaps it will come in handy someday.
1287
- #++
1288
- def close
1289
- @conn.close
1290
- @conn = nil
1291
- end
1292
-
1293
- # Internal: Reads and parses data from the configured connection.
1294
- #
1295
- # - syntax: the BER syntax to use to parse the read data with
1296
- #
1297
- # Returns basic BER objects.
1298
- def read(syntax = Net::LDAP::AsnSyntax)
1299
- instrument "read.net_ldap_connection", :syntax => syntax do |payload|
1300
- @conn.read_ber(syntax) do |id, content_length|
1301
- payload[:object_type_id] = id
1302
- payload[:content_length] = content_length
1303
- end
1304
- end
1305
- end
1306
- private :read
1307
-
1308
- # Internal: Writes the given packet to the configured connection.
1309
- #
1310
- # - packet: the BER data packet to write on the socket.
1311
- #
1312
- # Returns the return value from writing to the connection, which in some
1313
- # cases is the Integer number of bytes written to the socket.
1314
- def write(packet)
1315
- instrument "write.net_ldap_connection" do |payload|
1316
- payload[:content_length] = @conn.write(packet)
1317
- end
1318
- end
1319
- private :write
1320
-
1321
- def next_msgid
1322
- @msgid ||= 0
1323
- @msgid += 1
1324
- end
1325
-
1326
- def bind(auth)
1327
- instrument "bind.net_ldap_connection" do |payload|
1328
- payload[:method] = meth = auth[:method]
1329
- if [:simple, :anonymous, :anon].include?(meth)
1330
- bind_simple auth
1331
- elsif meth == :sasl
1332
- bind_sasl(auth)
1333
- elsif meth == :gss_spnego
1334
- bind_gss_spnego(auth)
1335
- else
1336
- raise Net::LDAP::LdapError, "Unsupported auth method (#{meth})"
1337
- end
1338
- end
1339
- end
1340
-
1341
- #--
1342
- # Implements a simple user/psw authentication. Accessed by calling #bind
1343
- # with a method of :simple or :anonymous.
1344
- #++
1345
- def bind_simple(auth)
1346
- user, psw = if auth[:method] == :simple
1347
- [auth[:username] || auth[:dn], auth[:password]]
1348
- else
1349
- ["", ""]
1350
- end
1351
-
1352
- raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw)
1353
-
1354
- msgid = next_msgid.to_ber
1355
- request = [LdapVersion.to_ber, user.to_ber,
1356
- psw.to_ber_contextspecific(0)].to_ber_appsequence(0)
1357
- request_pkt = [msgid, request].to_ber_sequence
1358
- write request_pkt
1359
-
1360
- (be = read and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
1361
-
1362
- pdu
1363
- end
1364
-
1365
- #--
1366
- # Required parameters: :mechanism, :initial_credential and
1367
- # :challenge_response
1368
- #
1369
- # Mechanism is a string value that will be passed in the SASL-packet's
1370
- # "mechanism" field.
1371
- #
1372
- # Initial credential is most likely a string. It's passed in the initial
1373
- # BindRequest that goes to the server. In some protocols, it may be empty.
1374
- #
1375
- # Challenge-response is a Ruby proc that takes a single parameter and
1376
- # returns an object that will typically be a string. The
1377
- # challenge-response block is called when the server returns a
1378
- # BindResponse with a result code of 14 (saslBindInProgress). The
1379
- # challenge-response block receives a parameter containing the data
1380
- # returned by the server in the saslServerCreds field of the LDAP
1381
- # BindResponse packet. The challenge-response block may be called multiple
1382
- # times during the course of a SASL authentication, and each time it must
1383
- # return a value that will be passed back to the server as the credential
1384
- # data in the next BindRequest packet.
1385
- #++
1386
- def bind_sasl(auth)
1387
- mech, cred, chall = auth[:mechanism], auth[:initial_credential],
1388
- auth[:challenge_response]
1389
- raise Net::LDAP::LdapError, "Invalid binding information" unless (mech && cred && chall)
1390
-
1391
- n = 0
1392
- loop {
1393
- msgid = next_msgid.to_ber
1394
- sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
1395
- request = [LdapVersion.to_ber, "".to_ber, sasl].to_ber_appsequence(0)
1396
- request_pkt = [msgid, request].to_ber_sequence
1397
- write request_pkt
1398
-
1399
- (be = read and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
1400
- return pdu unless pdu.result_code == 14 # saslBindInProgress
1401
- raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
1402
-
1403
- cred = chall.call(pdu.result_server_sasl_creds)
1404
- }
1405
-
1406
- raise Net::LDAP::LdapError, "why are we here?"
1407
- end
1408
- private :bind_sasl
1409
-
1410
- #--
1411
- # PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
1412
- # Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
1413
- # integrate it without introducing an external dependency.
1414
- #
1415
- # This authentication method is accessed by calling #bind with a :method
1416
- # parameter of :gss_spnego. It requires :username and :password
1417
- # attributes, just like the :simple authentication method. It performs a
1418
- # GSS-SPNEGO authentication with the server, which is presumed to be a
1419
- # Microsoft Active Directory.
1420
- #++
1421
- def bind_gss_spnego(auth)
1422
- require 'ntlm'
1423
-
1424
- user, psw = [auth[:username] || auth[:dn], auth[:password]]
1425
- raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw)
1426
-
1427
- nego = proc { |challenge|
1428
- t2_msg = NTLM::Message.parse(challenge)
1429
- t3_msg = t2_msg.response({ :user => user, :password => psw },
1430
- { :ntlmv2 => true })
1431
- t3_msg.serialize
1432
- }
1433
-
1434
- bind_sasl(:method => :sasl, :mechanism => "GSS-SPNEGO",
1435
- :initial_credential => NTLM::Message::Type1.new.serialize,
1436
- :challenge_response => nego)
1437
- end
1438
- private :bind_gss_spnego
1439
-
1440
-
1441
- #--
1442
- # Allow the caller to specify a sort control
1443
- #
1444
- # The format of the sort control needs to be:
1445
- #
1446
- # :sort_control => ["cn"] # just a string
1447
- # or
1448
- # :sort_control => [["cn", "matchingRule", true]] #attribute, matchingRule, direction (true / false)
1449
- # or
1450
- # :sort_control => ["givenname","sn"] #multiple strings or arrays
1451
- #
1452
- def encode_sort_controls(sort_definitions)
1453
- return sort_definitions unless sort_definitions
1454
-
1455
- sort_control_values = sort_definitions.map do |control|
1456
- control = Array(control) # if there is only an attribute name as a string then infer the orderinrule and reverseorder
1457
- control[0] = String(control[0]).to_ber,
1458
- control[1] = String(control[1]).to_ber,
1459
- control[2] = (control[2] == true).to_ber
1460
- control.to_ber_sequence
1461
- end
1462
- sort_control = [
1463
- Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
1464
- false.to_ber,
1465
- sort_control_values.to_ber_sequence.to_s.to_ber
1466
- ].to_ber_sequence
1467
- end
1468
-
1469
- #--
1470
- # Alternate implementation, this yields each search entry to the caller as
1471
- # it are received.
1472
- #
1473
- # TODO: certain search parameters are hardcoded.
1474
- # TODO: if we mis-parse the server results or the results are wrong, we
1475
- # can block forever. That's because we keep reading results until we get a
1476
- # type-5 packet, which might never come. We need to support the time-limit
1477
- # in the protocol.
1478
- #++
1479
- def search(args = {})
1480
- search_filter = (args && args[:filter]) ||
1481
- Net::LDAP::Filter.eq("objectclass", "*")
1482
- search_filter = Net::LDAP::Filter.construct(search_filter) if search_filter.is_a?(String)
1483
- search_base = (args && args[:base]) || "dc=example, dc=com"
1484
- search_attributes = ((args && args[:attributes]) || []).map { |attr| attr.to_s.to_ber}
1485
- return_referrals = args && args[:return_referrals] == true
1486
- sizelimit = (args && args[:size].to_i) || 0
1487
- raise Net::LDAP::LdapError, "invalid search-size" unless sizelimit >= 0
1488
- paged_searches_supported = (args && args[:paged_searches_supported])
1489
-
1490
- attributes_only = (args and args[:attributes_only] == true)
1491
- scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree
1492
- raise Net::LDAP::LdapError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope)
1493
-
1494
- sort_control = encode_sort_controls(args.fetch(:sort_controls){ false })
1495
-
1496
- deref = args[:deref] || Net::LDAP::DerefAliases_Never
1497
- raise Net::LDAP::LdapError.new( "invalid alias dereferencing value" ) unless Net::LDAP::DerefAliasesArray.include?(deref)
1498
-
1499
-
1500
- # An interesting value for the size limit would be close to A/D's
1501
- # built-in page limit of 1000 records, but openLDAP newer than version
1502
- # 2.2.0 chokes on anything bigger than 126. You get a silent error that
1503
- # is easily visible by running slapd in debug mode. Go figure.
1504
- #
1505
- # Changed this around 06Sep06 to support a caller-specified search-size
1506
- # limit. Because we ALWAYS do paged searches, we have to work around the
1507
- # problem that it's not legal to specify a "normal" sizelimit (in the
1508
- # body of the search request) that is larger than the page size we're
1509
- # requesting. Unfortunately, I have the feeling that this will break
1510
- # with LDAP servers that don't support paged searches!!!
1511
- #
1512
- # (Because we pass zero as the sizelimit on search rounds when the
1513
- # remaining limit is larger than our max page size of 126. In these
1514
- # cases, I think the caller's search limit will be ignored!)
1515
- #
1516
- # CONFIRMED: This code doesn't work on LDAPs that don't support paged
1517
- # searches when the size limit is larger than 126. We're going to have
1518
- # to do a root-DSE record search and not do a paged search if the LDAP
1519
- # doesn't support it. Yuck.
1520
- rfc2696_cookie = [126, ""]
1521
- result_pdu = nil
1522
- n_results = 0
1523
-
1524
- instrument "search.net_ldap_connection",
1525
- :filter => search_filter,
1526
- :base => search_base,
1527
- :scope => scope,
1528
- :limit => sizelimit,
1529
- :sort => sort_control,
1530
- :referrals => return_referrals,
1531
- :deref => deref,
1532
- :attributes => search_attributes do |payload|
1533
- loop do
1534
- # should collect this into a private helper to clarify the structure
1535
- query_limit = 0
1536
- if sizelimit > 0
1537
- if paged_searches_supported
1538
- query_limit = (((sizelimit - n_results) < 126) ? (sizelimit -
1539
- n_results) : 0)
1540
- else
1541
- query_limit = sizelimit
1542
- end
1543
- end
1544
-
1545
- request = [
1546
- search_base.to_ber,
1547
- scope.to_ber_enumerated,
1548
- deref.to_ber_enumerated,
1549
- query_limit.to_ber, # size limit
1550
- 0.to_ber,
1551
- attributes_only.to_ber,
1552
- search_filter.to_ber,
1553
- search_attributes.to_ber_sequence
1554
- ].to_ber_appsequence(3)
1555
-
1556
- # rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
1557
- # this breaks when calling to_ber. (Can't force binary data to UTF-8)
1558
- # we have to disable paging (even though server supports it) to get around this...
1559
-
1560
- controls = []
1561
- controls <<
1562
- [
1563
- Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
1564
- # Criticality MUST be false to interoperate with normal LDAPs.
1565
- false.to_ber,
1566
- rfc2696_cookie.map{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
1567
- ].to_ber_sequence if paged_searches_supported
1568
- controls << sort_control if sort_control
1569
- controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
1570
-
1571
- pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
1572
- write pkt
1573
-
1574
- result_pdu = nil
1575
- controls = []
1576
-
1577
- while (be = read) && (pdu = Net::LDAP::PDU.new(be))
1578
- case pdu.app_tag
1579
- when Net::LDAP::PDU::SearchReturnedData
1580
- n_results += 1
1581
- yield pdu.search_entry if block_given?
1582
- when Net::LDAP::PDU::SearchResultReferral
1583
- if return_referrals
1584
- if block_given?
1585
- se = Net::LDAP::Entry.new
1586
- se[:search_referrals] = (pdu.search_referrals || [])
1587
- yield se
1588
- end
1589
- end
1590
- when Net::LDAP::PDU::SearchResult
1591
- result_pdu = pdu
1592
- controls = pdu.result_controls
1593
- if return_referrals && pdu.result_code == 10
1594
- if block_given?
1595
- se = Net::LDAP::Entry.new
1596
- se[:search_referrals] = (pdu.search_referrals || [])
1597
- yield se
1598
- end
1599
- end
1600
- break
1601
- else
1602
- raise Net::LDAP::LdapError, "invalid response-type in search: #{pdu.app_tag}"
1603
- end
1604
- end
1605
-
1606
- # count number of pages of results
1607
- payload[:page_count] ||= 0
1608
- payload[:page_count] += 1
1609
-
1610
- # When we get here, we have seen a type-5 response. If there is no
1611
- # error AND there is an RFC-2696 cookie, then query again for the next
1612
- # page of results. If not, we're done. Don't screw this up or we'll
1613
- # break every search we do.
1614
- #
1615
- # Noticed 02Sep06, look at the read_ber call in this loop, shouldn't
1616
- # that have a parameter of AsnSyntax? Does this just accidentally
1617
- # work? According to RFC-2696, the value expected in this position is
1618
- # of type OCTET STRING, covered in the default syntax supported by
1619
- # read_ber, so I guess we're ok.
1620
- more_pages = false
1621
- if result_pdu.result_code == 0 and controls
1622
- controls.each do |c|
1623
- if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
1624
- # just in case some bogus server sends us more than 1 of these.
1625
- more_pages = false
1626
- if c.value and c.value.length > 0
1627
- cookie = c.value.read_ber[1]
1628
- if cookie and cookie.length > 0
1629
- rfc2696_cookie[1] = cookie
1630
- more_pages = true
1631
- end
1632
- end
1633
- end
1634
- end
1635
- end
1636
-
1637
- break unless more_pages
1638
- end # loop
1639
-
1640
- # track total result count
1641
- payload[:result_count] = n_results
1642
-
1643
- result_pdu || OpenStruct.new(:status => :failure, :result_code => 1, :message => "Invalid search")
1644
- end # instrument
1645
- end
1646
-
1647
- MODIFY_OPERATIONS = { #:nodoc:
1648
- :add => 0,
1649
- :delete => 1,
1650
- :replace => 2
1651
- }
1652
-
1653
- def self.modify_ops(operations)
1654
- ops = []
1655
- if operations
1656
- operations.each { |op, attrib, values|
1657
- # TODO, fix the following line, which gives a bogus error if the
1658
- # opcode is invalid.
1659
- op_ber = MODIFY_OPERATIONS[op.to_sym].to_ber_enumerated
1660
- values = [ values ].flatten.map { |v| v.to_ber if v }.to_ber_set
1661
- values = [ attrib.to_s.to_ber, values ].to_ber_sequence
1662
- ops << [ op_ber, values ].to_ber
1663
- }
1664
- end
1665
- ops
1666
- end
1667
-
1668
- #--
1669
- # TODO: need to support a time limit, in case the server fails to respond.
1670
- # TODO: We're throwing an exception here on empty DN. Should return a
1671
- # proper error instead, probaby from farther up the chain.
1672
- # TODO: If the user specifies a bogus opcode, we'll throw a confusing
1673
- # error here ("to_ber_enumerated is not defined on nil").
1674
- #++
1675
- def modify(args)
1676
- modify_dn = args[:dn] or raise "Unable to modify empty DN"
1677
- ops = self.class.modify_ops args[:operations]
1678
- request = [ modify_dn.to_ber,
1679
- ops.to_ber_sequence ].to_ber_appsequence(6)
1680
- pkt = [ next_msgid.to_ber, request ].to_ber_sequence
1681
- write pkt
1682
-
1683
- (be = read) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == Net::LDAP::PDU::ModifyResponse) or raise Net::LDAP::LdapError, "response missing or invalid"
1684
-
1685
- pdu
1686
- end
1687
-
1688
- #--
1689
- # TODO: need to support a time limit, in case the server fails to respond.
1690
- # Unlike other operation-methods in this class, we return a result hash
1691
- # rather than a simple result number. This is experimental, and eventually
1692
- # we'll want to do this with all the others. The point is to have access
1693
- # to the error message and the matched-DN returned by the server.
1694
- #++
1695
- def add(args)
1696
- add_dn = args[:dn] or raise Net::LDAP::LdapError, "Unable to add empty DN"
1697
- add_attrs = []
1698
- a = args[:attributes] and a.each { |k, v|
1699
- add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
1700
- }
1701
-
1702
- request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(8)
1703
- pkt = [next_msgid.to_ber, request].to_ber_sequence
1704
- write pkt
1705
-
1706
- (be = read) &&
1707
- (pdu = Net::LDAP::PDU.new(be)) &&
1708
- (pdu.app_tag == Net::LDAP::PDU::AddResponse) or
1709
- raise Net::LDAP::LdapError, "response missing or invalid"
1710
-
1711
- pdu
1712
- end
1713
-
1714
- #--
1715
- # TODO: need to support a time limit, in case the server fails to respond.
1716
- #++
1717
- def rename(args)
1718
- old_dn = args[:olddn] or raise "Unable to rename empty DN"
1719
- new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
1720
- delete_attrs = args[:delete_attributes] ? true : false
1721
- new_superior = args[:new_superior]
1722
-
1723
- request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
1724
- request << new_superior.to_ber_contextspecific(0) unless new_superior == nil
1725
-
1726
- pkt = [next_msgid.to_ber, request.to_ber_appsequence(12)].to_ber_sequence
1727
- write pkt
1728
-
1729
- (be = read) &&
1730
- (pdu = Net::LDAP::PDU.new( be )) && (pdu.app_tag == Net::LDAP::PDU::ModifyRDNResponse) or
1731
- raise Net::LDAP::LdapError.new( "response missing or invalid" )
1732
-
1733
- pdu
1734
- end
1735
-
1736
- #--
1737
- # TODO, need to support a time limit, in case the server fails to respond.
1738
- #++
1739
- def delete(args)
1740
- dn = args[:dn] or raise "Unable to delete empty DN"
1741
- controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
1742
- request = dn.to_s.to_ber_application_string(10)
1743
- pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
1744
- write pkt
1745
-
1746
- (be = read) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == Net::LDAP::PDU::DeleteResponse) or raise Net::LDAP::LdapError, "response missing or invalid"
1747
-
1748
- pdu
1749
- end
1750
- end # class Connection