Compare commits

...

56 Commits

Author SHA1 Message Date
Denis Knauf 7438a2ab45 One LdapError-class => Specific errors for every exception. 2013-03-16 13:10:02 +01:00
Rory O'Connell 8a182675f4 Merge pull request #46 from epeli/patch-1
Add result string 19 Constraint Violation
2012-08-28 15:54:21 -07:00
Rory O'Connell ccd7b6da5b Merge pull request #41 from justcfx2u/master
Fall back if string cannot be encoded
2012-08-28 15:53:15 -07:00
Rory O'Connell 8acd4acc5b Merge pull request #39 from aspgems/master
Changed back return values in API methods to match what documentation says
2012-08-28 15:53:03 -07:00
Rory O'Connell 8ddb2d7c84 Merge pull request #35 from MarkCDSys/master
Parse UTF-8 special-chars in Net::LDAP::Filter.construct()
2012-08-28 15:52:52 -07:00
Rory O'Connell 9de9bc755b Merge pull request #34 from jessehub/master
salted sha1 for password generator()
2012-08-28 15:52:40 -07:00
Rory O'Connell b6d9fbebfd Merge pull request #33 from DavidJLee/master
New filter type to allow searching of binary data
2012-08-28 15:51:53 -07:00
Esa-Matti Suuronen 717132f224 Add result string 19 Constraint Violation
As documented in http://www.openldap.org/doc/admin24/appendix-ldap-result-codes.html
2012-08-20 10:58:23 +03:00
Harold A. Jones II 656039cee0 Gracefully fall back if string cannot be encoded. 2012-04-30 22:03:55 -04:00
Francisco R. Santos d6aa24ebea Changed back return values in API methods to match what documentation says 2012-04-18 20:11:11 +02:00
Markus Bucher b63e5c4930 Added common UTF-8 special characters to filter-values 2012-03-20 17:41:00 +01:00
Jesse Callaway aa677d0471 Added salted sha1 support to password generator. Successfully tested with OpenLDAP 2.4 2012-03-07 19:03:32 -05:00
Rory OConnell 51597eae9a Correction for rename #32 2012-02-28 21:29:37 -08:00
Rory OConnell a102054bbf Version bump 2012-02-28 21:29:02 -08:00
Rory OConnell 4ab764558f Merge 2012-02-28 20:48:31 -08:00
David J. Lee 9fa0b982b7 Added parens around args for rename. 2012-02-24 15:19:18 -06:00
David J. Lee 9a9d5f0742 Added configuration option to force paged searches off 2012-02-24 14:01:53 -06:00
David J. Lee c46c93777e Added a new filter type bineq that will create an equality filter and NOT force
convert data to UTF-8. This is required for proper binary data filters in Microsoft
Active Directory.
2012-02-24 09:55:17 -06:00
Rory OConnell 3345c58dfb Fixing broken release 2012-02-15 11:54:46 -08:00
Rory OConnell 373304d812 Fixing gemspec 2012-02-14 21:05:12 -08:00
Rory OConnell cba57eb50d URL change 2012-02-14 20:52:08 -08:00
Rory OConnell 5467ecf6cd Words 2012-02-14 20:51:47 -08:00
Rory OConnell 995ddaa4e2 Version bump 2012-02-14 20:51:15 -08:00
MichaelBaker ad4493b104 Update net-ldap.gemspec 2012-01-30 20:10:29 -08:00
MichaelBaker 09e372ee63 Update net-ldap.gemspec 2012-01-30 20:07:45 -08:00
Christopher Dwan b13c71d265 Merge pull request #3 from partnerpedia/add_sort_control
Add sort control
2012-01-12 16:51:36 -08:00
Chris Dwan 4c24b4ea36 Added encoding of sort controls if passed in as an option to the search 2012-01-12 14:50:12 -08:00
Rory O'Connell 76a81cce4a Merge pull request #26 from ManageIQ/continuation-reference-processing
Added Continuation Reference Processing as defined in section 4.5.3 of RFC 2251 (http://www.ietf.org/rfc/rfc2251.txt)
2011-12-28 12:52:22 -08:00
Oleg Barenboim 8a106ca64f Added Continuation Reference Processing as defined in section 4.5.3 of RFC 2251 (http://www.ietf.org/rfc/rfc2251.txt) 2011-12-28 15:29:40 -05:00
Rory O'Connell a1bf790784 Merge pull request #18 from doitian/issue/17_bererror_unsupported_object_type_id_139
Do not add controls when it is empty. Fixed #17
2011-12-04 22:32:32 -08:00
Ivar Vasara b94bba9773 Merge pull request #2 from partnerpedia/better_response_message
Better response message
2011-12-01 11:51:26 -08:00
Michael Baker 40f0e1857e Add success and failure methods. 2011-11-30 20:03:02 +00:00
Michael Baker 2763040162 Return PDU instead of result code 2011-11-29 22:24:34 +00:00
Michael Baker 63db8c836a Bring back the Net::LDAP namespace 2011-11-17 23:32:03 +00:00
radixhound 528ef30801 Merge pull request #1 from partnerpedia/add_delete_tree
added the ability to do a delete_tree
2011-11-17 14:42:37 -08:00
Chris Dwan 58bd212918 remove Gemfile.lock 2011-11-17 14:25:00 -08:00
Chris Dwan b6b7985d6e fixes based on comments for pull request 2011-11-17 14:23:41 -08:00
Chris Dwan 463ac436a8 added the ability to do a delete_tree 2011-11-16 16:36:39 -08:00
Rory O'Connell 6bb9fa6ae6 Merge pull request #24 from danabr/default_search_behaviour
search should return result set if :return_result is unspecified (nil).
2011-10-07 05:14:08 -07:00
Daniel Abrahamsson 2a74577d5f search should return result set if :return_result is unspecified (nil).
Corrects incorrect behaviour introduced in a4819e525f
2011-10-07 15:56:55 +02:00
Rory O'Connell 5344d73543 Merge pull request #23 from danabr/correct_return_value_from_search
Correct return value from search
2011-09-24 11:33:38 -07:00
Rory O'Connell dac73f46d2 Merge pull request #22 from danabr/ldap_strings_are_utf8
Make Net::LDAP use UTF-8 strings
2011-09-24 11:33:10 -07:00
Rory O'Connell 6d7be69653 Merge pull request #20 from danabr/rename_fix
Rename fix
2011-09-24 11:32:28 -07:00
Rory O'Connell 5c3cbb7fe6 Merge pull request #16 from mcarpenter/master
Issue #15: LDIF continuation character
2011-09-24 11:31:32 -07:00
Daniel Abrahamsson 2336188503 No need to pass empty block to search 2011-09-22 16:19:12 +02:00
Daniel Abrahamsson a4819e525f Fix incorrect return value from search when :return_result => true and and search fails 2011-09-22 15:55:40 +02:00
Daniel Abrahamsson 42bdeb93d8 Add test case showing incorrect behaviour for failed searches 2011-09-22 15:55:13 +02:00
Martin Carpenter d2e00dfd58 Test single tab is not continued 2011-09-10 10:39:05 +02:00
Daniel Abrahamsson 3a8b8a2e00 LDAP uses UTF-8 strings 2011-09-09 19:16:30 +02:00
Daniel Abrahamsson c90821a7bd Fixed whitespace issues in lib/net/ldap.rb 2011-08-25 13:16:45 +02:00
Daniel Abrahamsson c11ec44258 Bugfix: rename failed because of unqualified constants 2011-08-25 13:14:10 +02:00
Ian Yang f102f50d9c Do not add controls when it is empty. Fixed #17
Some LDAP servers, such as ApacheDS, consider as invalid protocol, if controls
is an empty array.
2011-08-10 20:18:27 +08:00
Martin Carpenter 41bee0a690 Fix LDIF contination to single elided space
RFC 2849 http://tools.ietf.org/html/rfc2849:

SPACE                    = %x20
                           ; ASCII SP, space
...

2)  Any non-empty line, including comment lines, in an LDIF file
    MAY be folded by inserting a line separator (SEP) and a SPACE.
    Folding MUST NOT occur before the first character of the line.
    In other words, folding a line into two lines, the first of
    which is empty, is not permitted. Any line that begins with a
    single space MUST be treated as a continuation of the previous
    (non-empty) line. When joining folded lines, exactly one space
    character at the beginning of each continued line must be
    discarded. Implementations SHOULD NOT fold lines in the middle
    of a multi-byte UTF-8 character.
2011-07-23 03:31:42 +02:00
Martin Carpenter 41b230d9bc Fix identation 2011-07-23 03:26:37 +02:00
Rory O'Connell 7dd6c3a107 Merge pull request #3 from dulanov/master
Incorrect response with return_result=false in Net::LDAP.search
2011-05-29 13:54:56 -07:00
dulanov c9bd8b4b3d fix incorrect respone with return_result=false 2011-05-27 18:24:52 +04:00
22 changed files with 530 additions and 193 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ publish/
coverage/
coverage.info
.rake_tasks~
Gemfile.lock

View File

@ -19,3 +19,4 @@ Contributions since:
* Derek Harmel (derekharmel)
* Erik Hetzner (egh)
* nowhereman
* David J. Lee (DavidJLee)

2
Gemfile Normal file
View File

@ -0,0 +1,2 @@
source :rubygems
gemspec

View File

@ -1,3 +1,17 @@
=== Net::LDAP 0.3.1 / 2012-02-15
* Bug Fixes:
* Bundler should now work again
=== Net::LDAP 0.3.0 / 2012-02-14
* Major changes:
* Now uses UTF-8 strings instead of ASCII-8 per the LDAP RFC
* Major Enhancements:
* Adding continuation reference processing
* Bug Fixes:
* Fixes usupported object type #139
* Fixes Net::LDAP namespace errors
* Return nil instead of an empty array if the search fails
=== Net::LDAP 0.2.2 / 2011-03-26
* Bug Fixes:
* Fixed the call to Net::LDAP.modify_ops from Net::LDAP#modify.

View File

@ -6,7 +6,6 @@ require 'hoe'
Hoe.plugin :doofus
Hoe.plugin :git
Hoe.plugin :gemspec
Hoe.plugin :rubyforge
Hoe.spec 'net-ldap' do |spec|
spec.rubyforge_name = spec.name
@ -20,7 +19,7 @@ Hoe.spec 'net-ldap' do |spec|
spec.remote_rdoc_dir = ''
spec.rsync_args << ' --exclude=statsvn/'
spec.url = %W(http://net-ldap.rubyforge.org/ https://github.com/ruby-ldap/ruby-net-ldap)
spec.url = %W(http://rubyldap.com/ https://github.com/ruby-ldap/ruby-net-ldap)
spec.history_file = 'History.rdoc'
spec.readme_file = 'README.rdoc'

View File

@ -106,7 +106,7 @@ module Net # :nodoc:
# <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
# </table>
module BER
VERSION = '0.2.2'
VERSION = '0.4.0'
##
# Used for BER-encoding the length and content bytes of a Fixnum integer
@ -295,6 +295,8 @@ class Net::BER::BerIdentifiedString < String
attr_accessor :ber_identifier
def initialize args
super args
# LDAP uses UTF-8 encoded strings
force_encoding('UTF-8') if respond_to?(:encoding)
end
end

View File

@ -79,4 +79,18 @@ module Net::BER::Extensions::Array
oid = ary.pack("w*")
[6, oid.length].pack("CC") + oid
end
##
# Converts an array into a set of ber control codes
# The expected format is [[control_oid, criticality, control_value(optional)]]
# [['1.2.840.113556.1.4.805',true]]
#
def to_ber_control
#if our array does not contain at least one array then wrap it in an array before going forward
ary = self[0].kind_of?(Array) ? self : [self]
ary = ary.collect do |control_sequence|
control_sequence.collect{|element| element.to_ber}.to_ber_sequence.reject_empty_ber_arrays
end
ary.to_ber_sequence.reject_empty_ber_arrays
end
end

View File

@ -12,9 +12,33 @@ module Net::BER::Extensions::String
# User code should call either #to_ber_application_string or
# #to_ber_contextspecific.
def to_ber(code = 0x04)
[code].pack('C') + length.to_ber_length_encoding + self
raw_string = raw_utf8_encoded
[code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
end
##
# Converts a string to a BER string but does *not* encode to UTF-8 first.
# This is required for proper representation of binary data for Microsoft
# Active Directory
def to_ber_bin(code = 0x04)
[code].pack('C') + length.to_ber_length_encoding + self
end
def raw_utf8_encoded
if self.respond_to?(:encode)
# Strings should be UTF-8 encoded according to LDAP.
# However, the BER code is not necessarily valid UTF-8
begin
self.encode('UTF-8').force_encoding('ASCII-8BIT')
rescue Encoding::UndefinedConversionError
self
end
else
self
end
end
private :raw_utf8_encoded
##
# Creates an application-specific BER string encoded value with the
# provided syntax code value.
@ -34,15 +58,19 @@ module Net::BER::Extensions::String
def read_ber(syntax = nil)
StringIO.new(self).read_ber(syntax)
end
##
# Destructively reads a BER object from the string.
# Destructively reads a BER object from the string.
def read_ber!(syntax = nil)
io = StringIO.new(self)
result = io.read_ber(syntax)
self.slice!(0...io.pos)
return result
end
def reject_empty_ber_arrays
self.gsub(/0\000/n,'')
end
end

View File

@ -241,9 +241,36 @@ require 'net/ldap/entry'
# and then keeps it open while it executes a user-supplied block.
# Net::LDAP#open closes the connection on completion of the block.
class Net::LDAP
VERSION = "0.2.2"
VERSION = "0.4.0"
class LdapError < StandardError; end
class AlreadyOpenedError < LdapError; end
class SocketError < LdapError; end
class ConnectionRefusedError < LdapError; end
class NoOpenSSLError < LdapError; end
class NoStartTLSResultError < LdapError; end
class StartTLSError < LdapError; end
class EncryptionUnsupportedError < LdapError; end
class EncMethodUnsupportedError < LdapError; end
class AuthMethodUnsupportedError < LdapError; end
class BindingInformationInvalidError < LdapError; end
class NoBindResultError < LdapError; end
class SASLChallengeOverflowError < LdapError; end
class SearchSizeInvalidError < LdapError; end
class SearchScopeInvalidError < LdapError; end
class ResponseTypeInvalidError < LdapError; end
class ResponseMissingOrInvalidError < LdapError; end
class EmptyDNError < LdapError; end
class HashTypeUnsupportedError < LdapError; end
class OperatorError < LdapError; end
class SubstringFilterError < LdapError; end
class SearchFilterError < LdapError; end
class BERInvalidError < LdapError; end
class SearchFilterTypeUnknownError < LdapError; end
class BadAttributeError < LdapError; end
class FilterTypeUnknownError < LdapError; end
class FilterSyntaxInvalidError < LdapError; end
class EntryOverflowError < LdapError; end
SearchScope_BaseObject = 0
SearchScope_SingleLevel = 1
@ -308,6 +335,7 @@ class Net::LDAP
DefaultPort = 389
DefaultAuth = { :method => :anonymous }
DefaultTreebase = "dc=com"
DefaultForceNoPage = false
StartTlsOid = "1.3.6.1.4.1.1466.20037"
@ -317,10 +345,12 @@ class Net::LDAP
2 => "Protocol Error",
3 => "Time Limit Exceeded",
4 => "Size Limit Exceeded",
10 => "Referral",
12 => "Unavailable crtical extension",
14 => "saslBindInProgress",
16 => "No Such Attribute",
17 => "Undefined Attribute Type",
19 => "Constraint Violation",
20 => "Attribute or Value Exists",
32 => "No Such Object",
34 => "Invalid DN Syntax",
@ -334,8 +364,11 @@ class Net::LDAP
68 => "Entry Already Exists"
}
module LdapControls
PagedResults = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
module LDAPControls
PAGED_RESULTS = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
SORT_REQUEST = "1.2.840.113556.1.4.473"
SORT_RESPONSE = "1.2.840.113556.1.4.474"
DELETE_TREE = "1.2.840.113556.1.4.805"
end
def self.result2string(code) #:nodoc:
@ -369,6 +402,8 @@ class Net::LDAP
# specifying the Hash {:method => :simple_tls}. There is a fairly large
# range of potential values that may be given for this parameter. See
# #encryption for details.
# * :force_no_page => Set to true to prevent paged results even if your
# server says it supports them. This is a fix for MS Active Directory
#
# Instantiating a Net::LDAP object does <i>not</i> result in network
# traffic to the LDAP server. It simply stores the connection and binding
@ -379,6 +414,7 @@ class Net::LDAP
@verbose = false # Make this configurable with a switch on the class.
@auth = args[:auth] || DefaultAuth
@base = args[:base] || DefaultTreebase
@force_no_page = args[:force_no_page] || DefaultForceNoPage
encryption args[:encryption] # may be nil
if pr = @auth[:password] and pr.respond_to?(:call)
@ -515,15 +551,17 @@ class Net::LDAP
# response codes instead of a simple numeric code.
#++
def get_operation_result
result = @result
result = result.result if result.is_a?(Net::LDAP::PDU)
os = OpenStruct.new
if @result.is_a?(Hash)
if result.is_a?(Hash)
# We might get a hash of LDAP response codes instead of a simple
# numeric code.
os.code = (@result[:resultCode] || "").to_i
os.error_message = @result[:errorMessage]
os.matched_dn = @result[:matchedDN]
elsif @result
os.code = @result
os.code = (result[:resultCode] || "").to_i
os.error_message = result[:errorMessage]
os.matched_dn = result[:matchedDN]
elsif result
os.code = result
else
os.code = 0
end
@ -552,7 +590,7 @@ class Net::LDAP
# anything with the bind results. We then pass self to the caller's
# block, where he will execute his LDAP operations. Of course they will
# all generate auth failures if the bind was unsuccessful.
raise Net::LDAP::LdapError, "Open already in progress" if @open_connection
raise Net::LDAP::AlreadyOpenedError, "Open already in progress" if @open_connection
begin
@open_connection = Net::LDAP::Connection.new(:host => @host,
@ -619,7 +657,8 @@ class Net::LDAP
end
args[:base] ||= @base
result_set = (args and args[:return_result] == false) ? nil : []
return_result_set = args[:return_result] != false
result_set = return_result_set ? [] : nil
if @open_connection
@result = @open_connection.search(args) { |entry|
@ -627,11 +666,10 @@ class Net::LDAP
yield entry if block_given?
}
else
@result = 0
begin
conn = Net::LDAP::Connection.new(:host => @host, :port => @port,
:encryption => @encryption)
if (@result = conn.bind(args[:auth] || @auth)) == 0
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
@result = conn.search(args) { |entry|
result_set << entry if result_set
yield entry if block_given?
@ -642,7 +680,11 @@ class Net::LDAP
end
end
@result == 0 and result_set
if return_result_set
(!@result.nil? && @result.result_code == 0) ? result_set : nil
else
@result.success?
end
end
# #bind connects to an LDAP server and requests authentication based on
@ -715,7 +757,7 @@ class Net::LDAP
end
end
@result == 0
@result.success?
end
# #bind_as is for testing authentication credentials.
@ -810,14 +852,14 @@ class Net::LDAP
begin
conn = Connection.new(:host => @host, :port => @port,
:encryption => @encryption)
if (@result = conn.bind(args[:auth] || @auth)) == 0
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
@result = conn.add(args)
end
ensure
conn.close if conn
end
end
@result == 0
@result.success?
end
# Modifies the attribute values of a particular entry on the LDAP
@ -908,14 +950,15 @@ class Net::LDAP
begin
conn = Connection.new(:host => @host, :port => @port,
:encryption => @encryption)
if (@result = conn.bind(args[:auth] || @auth)) == 0
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
@result = conn.modify(args)
end
ensure
conn.close if conn
end
end
@result == 0
@result.success?
end
# Add a value to an attribute. Takes the full DN of the entry to modify,
@ -979,14 +1022,14 @@ class Net::LDAP
begin
conn = Connection.new(:host => @host, :port => @port,
:encryption => @encryption)
if (@result = conn.bind(args[:auth] || @auth)) == 0
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
@result = conn.rename(args)
end
ensure
conn.close if conn
end
end
@result == 0
@result.success?
end
alias_method :modify_rdn, :rename
@ -1007,16 +1050,29 @@ class Net::LDAP
begin
conn = Connection.new(:host => @host, :port => @port,
:encryption => @encryption)
if (@result = conn.bind(args[:auth] || @auth)) == 0
if (@result = conn.bind(args[:auth] || @auth)).result_code == 0
@result = conn.delete(args)
end
ensure
conn.close
end
end
@result == 0
@result.success?
end
# Delete an entry from the LDAP directory along with all subordinate entries.
# the regular delete method will fail to delete an entry if it has subordinate
# entries. This method sends an extra control code to tell the LDAP server
# to do a tree delete. ('1.2.840.113556.1.4.805')
#
# Returns True or False to indicate whether the delete succeeded. Extended
# status information is available by calling #get_operation_result.
#
# dn = "mail=deleteme@example.com, ou=people, dc=example, dc=com"
# ldap.delete_tree :dn => dn
def delete_tree(args)
delete(args.merge(:control_codes => [[Net::LDAP::LDAPControls::DELETE_TREE, true]]))
end
# This method is experimental and subject to change. Return the rootDSE
# record from the LDAP server as a Net::LDAP::Entry, or an empty Entry if
# the server doesn't return the record.
@ -1086,8 +1142,12 @@ class Net::LDAP
# MUST refactor the root_dse call out.
#++
def paged_searches_supported?
# active directory returns that it supports paged results. However
# it returns binary data in the rfc2696_cookie which throws an
# encoding exception breaking searching.
return false if @force_no_page
@server_caps ||= search_root_dse
@server_caps[:supportedcontrol].include?(Net::LDAP::LdapControls::PagedResults)
@server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
end
end # class LDAP
@ -1101,9 +1161,9 @@ class Net::LDAP::Connection #:nodoc:
begin
@conn = TCPSocket.new(server[:host], server[:port])
rescue SocketError
raise Net::LDAP::LdapError, "No such address or other socket error."
raise Net::LDAP::SocketError, "No such address or other socket error."
rescue Errno::ECONNREFUSED
raise Net::LDAP::LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}."
raise Net::LDAP::ConnectionRefusedError, "Server #{server[:host]} refused connection on port #{server[:port]}."
end
if server[:encryption]
@ -1120,7 +1180,7 @@ class Net::LDAP::Connection #:nodoc:
end
def self.wrap_with_ssl(io)
raise Net::LDAP::LdapError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
ctx = OpenSSL::SSL::SSLContext.new
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
conn.connect
@ -1169,16 +1229,16 @@ class Net::LDAP::Connection #:nodoc:
request_pkt = [msgid, request].to_ber_sequence
@conn.write request_pkt
be = @conn.read_ber(Net::LDAP::AsnSyntax)
raise Net::LDAP::LdapError, "no start_tls result" if be.nil?
raise Net::LDAP::NoStartTLSResultError, "no start_tls result" if be.nil?
pdu = Net::LDAP::PDU.new(be)
raise Net::LDAP::LdapError, "no start_tls result" if pdu.nil?
raise Net::LDAP::NoStartTLSResultError, "no start_tls result" if pdu.nil?
if pdu.result_code.zero?
@conn = self.class.wrap_with_ssl(@conn)
else
raise Net::LDAP::LdapError, "start_tls failed: #{pdu.result_code}"
raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}"
end
else
raise Net::LDAP::LdapError, "unsupported encryption method #{args[:method]}"
raise Net::LDAP::EncryptionUnsupportedError, "unsupported encryption method #{args[:method]}"
end
end
@ -1206,7 +1266,7 @@ class Net::LDAP::Connection #:nodoc:
elsif meth == :gss_spnego
bind_gss_spnego(auth)
else
raise Net::LDAP::LdapError, "Unsupported auth method (#{meth})"
raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})"
end
end
@ -1221,7 +1281,7 @@ class Net::LDAP::Connection #:nodoc:
["", ""]
end
raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw)
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
msgid = next_msgid.to_ber
request = [LdapVersion.to_ber, user.to_ber,
@ -1229,9 +1289,9 @@ class Net::LDAP::Connection #:nodoc:
request_pkt = [msgid, request].to_ber_sequence
@conn.write request_pkt
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::NoBindResultError, "no bind result"
pdu.result_code
pdu
end
#--
@ -1258,7 +1318,7 @@ class Net::LDAP::Connection #:nodoc:
def bind_sasl(auth)
mech, cred, chall = auth[:mechanism], auth[:initial_credential],
auth[:challenge_response]
raise Net::LDAP::LdapError, "Invalid binding information" unless (mech && cred && chall)
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
n = 0
loop {
@ -1268,9 +1328,9 @@ class Net::LDAP::Connection #:nodoc:
request_pkt = [msgid, request].to_ber_sequence
@conn.write request_pkt
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::LdapError, "no bind result"
return pdu.result_code unless pdu.result_code == 14 # saslBindInProgress
raise Net::LDAP::LdapError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
(be = @conn.read_ber(Net::LDAP::AsnSyntax) and pdu = Net::LDAP::PDU.new(be)) or raise Net::LDAP::NoBindResultError, "no bind result"
return pdu unless pdu.result_code == 14 # saslBindInProgress
raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
cred = chall.call(pdu.result_server_sasl_creds)
}
@ -1294,7 +1354,7 @@ class Net::LDAP::Connection #:nodoc:
require 'ntlm'
user, psw = [auth[:username] || auth[:dn], auth[:password]]
raise Net::LDAP::LdapError, "Invalid binding information" unless (user && psw)
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
nego = proc { |challenge|
t2_msg = NTLM::Message.parse(challenge)
@ -1309,6 +1369,35 @@ class Net::LDAP::Connection #:nodoc:
end
private :bind_gss_spnego
#--
# Allow the caller to specify a sort control
#
# The format of the sort control needs to be:
#
# :sort_control => ["cn"] # just a string
# or
# :sort_control => [["cn", "matchingRule", true]] #attribute, matchingRule, direction (true / false)
# or
# :sort_control => ["givenname","sn"] #multiple strings or arrays
#
def encode_sort_controls(sort_definitions)
return sort_definitions unless sort_definitions
sort_control_values = sort_definitions.map do |control|
control = Array(control) # if there is only an attribute name as a string then infer the orderinrule and reverseorder
control[0] = String(control[0]).to_ber,
control[1] = String(control[1]).to_ber,
control[2] = (control[2] == true).to_ber
control.to_ber_sequence
end
sort_control = [
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
false.to_ber,
sort_control_values.to_ber_sequence.to_s.to_ber
].to_ber_sequence
end
#--
# Alternate implementation, this yields each search entry to the caller as
# it are received.
@ -1320,20 +1409,21 @@ class Net::LDAP::Connection #:nodoc:
# in the protocol.
#++
def search(args = {})
search_filter = (args && args[:filter]) ||
search_filter = (args && args[:filter]) ||
Net::LDAP::Filter.eq("objectclass", "*")
search_filter = Net::LDAP::Filter.construct(search_filter) if search_filter.is_a?(String)
search_base = (args && args[:base]) || "dc=example, dc=com"
search_attributes = ((args && args[:attributes]) || []).map { |attr| attr.to_s.to_ber}
return_referrals = args && args[:return_referrals] == true
sizelimit = (args && args[:size].to_i) || 0
raise Net::LDAP::LdapError, "invalid search-size" unless sizelimit >= 0
raise Net::LDAP::SearchSizeInvalidError, "invalid search-size" unless sizelimit >= 0
paged_searches_supported = (args && args[:paged_searches_supported])
attributes_only = (args and args[:attributes_only] == true)
scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree
raise Net::LDAP::LdapError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope)
raise Net::LDAP::SearchScopeInvalidError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope)
sort_control = encode_sort_controls(args.fetch(:sort_controls){ false })
# An interesting value for the size limit would be close to A/D's
# built-in page limit of 1000 records, but openLDAP newer than version
# 2.2.0 chokes on anything bigger than 126. You get a silent error that
@ -1355,7 +1445,7 @@ class Net::LDAP::Connection #:nodoc:
# to do a root-DSE record search and not do a paged search if the LDAP
# doesn't support it. Yuck.
rfc2696_cookie = [126, ""]
result_code = 0
result_pdu = nil
n_results = 0
loop {
@ -1381,20 +1471,25 @@ class Net::LDAP::Connection #:nodoc:
search_attributes.to_ber_sequence
].to_ber_appsequence(3)
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
# we have to disable paging (even though server supports it) to get around this...
controls = []
controls <<
[
Net::LDAP::LdapControls::PagedResults.to_ber,
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
# Criticality MUST be false to interoperate with normal LDAPs.
false.to_ber,
rfc2696_cookie.map{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
].to_ber_sequence if paged_searches_supported
controls = controls.to_ber_contextspecific(0)
controls << sort_control if sort_control
controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
pkt = [next_msgid.to_ber, request, controls].to_ber_sequence
pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
@conn.write pkt
result_code = 0
result_pdu = nil
controls = []
while (be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be))
@ -1411,11 +1506,18 @@ class Net::LDAP::Connection #:nodoc:
end
end
when 5 # search-result
result_code = pdu.result_code
result_pdu = pdu
controls = pdu.result_controls
if return_referrals && result_code == 10
if block_given?
se = Net::LDAP::Entry.new
se[:search_referrals] = (pdu.search_referrals || [])
yield se
end
end
break
else
raise Net::LDAP::LdapError, "invalid response-type in search: #{pdu.app_tag}"
raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}"
end
end
@ -1430,9 +1532,9 @@ class Net::LDAP::Connection #:nodoc:
# of type OCTET STRING, covered in the default syntax supported by
# read_ber, so I guess we're ok.
more_pages = false
if result_code == 0 and controls
if result_pdu.result_code == 0 and controls
controls.each do |c|
if c.oid == Net::LDAP::LdapControls::PagedResults
if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS
# just in case some bogus server sends us more than 1 of these.
more_pages = false
if c.value and c.value.length > 0
@ -1449,7 +1551,7 @@ class Net::LDAP::Connection #:nodoc:
break unless more_pages
} # loop
result_code
result_pdu || OpenStruct.new(:status => :failure, :result_code => 1, :message => "Invalid search")
end
MODIFY_OPERATIONS = { #:nodoc:
@ -1488,8 +1590,9 @@ class Net::LDAP::Connection #:nodoc:
pkt = [ next_msgid.to_ber, request ].to_ber_sequence
@conn.write pkt
(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"
pdu.result_code
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 7) or raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
pdu
end
#--
@ -1500,7 +1603,7 @@ class Net::LDAP::Connection #:nodoc:
# to the error message and the matched-DN returned by the server.
#++
def add(args)
add_dn = args[:dn] or raise Net::LDAP::LdapError, "Unable to add empty DN"
add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN"
add_attrs = []
a = args[:attributes] and a.each { |k, v|
add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
@ -1510,27 +1613,34 @@ class Net::LDAP::Connection #:nodoc:
pkt = [next_msgid.to_ber, request].to_ber_sequence
@conn.write pkt
(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"
pdu.result_code
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) &&
(pdu = Net::LDAP::PDU.new(be)) &&
(pdu.app_tag == 9) or
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
pdu
end
#--
# TODO: need to support a time limit, in case the server fails to respond.
#++
def rename args
def rename(args)
old_dn = args[:olddn] or raise "Unable to rename empty DN"
new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
delete_attrs = args[:delete_attributes] ? true : false
new_superior = args[:new_superior]
new_superior = args[:new_superior]
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
request << new_superior.to_ber_contextspecific(0) unless new_superior == nil
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber]
request << new_superior.to_ber unless new_superior == nil
pkt = [next_msgid.to_ber, request.to_ber_appsequence(12)].to_ber_sequence
@conn.write pkt
(be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 13) or raise LdapError.new( "response missing or invalid" )
pdu.result_code
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) &&
(pdu = Net::LDAP::PDU.new( be )) && (pdu.app_tag == 13) or
raise Net::LDAP::ResponseMissingOrInvalidError.new( "response missing or invalid" )
pdu
end
#--
@ -1538,12 +1648,13 @@ class Net::LDAP::Connection #:nodoc:
#++
def delete(args)
dn = args[:dn] or raise "Unable to delete empty DN"
controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
request = dn.to_s.to_ber_application_string(10)
pkt = [next_msgid.to_ber, request].to_ber_sequence
pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
@conn.write pkt
(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"
pdu.result_code
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 11) or raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
pdu
end
end # class Connection

View File

@ -117,8 +117,8 @@ class Net::LDAP::Dataset < Hash
while line
new_line = io.gets
if new_line =~ /^[\s]+/
line << " " << $'
if new_line =~ /^ /
line << $'
else
nextline = new_line

View File

@ -71,7 +71,7 @@ class Net::LDAP::Entry
return nil if ds.empty?
raise Net::LDAP::LdapError, "Too many LDIF entries" unless ds.size == 1
raise Net::LDAP::EntryOverflowError, "Too many LDIF entries" unless ds.size == 1
entry = ds.to_entries.first

View File

@ -23,11 +23,11 @@
class Net::LDAP::Filter
##
# Known filter types.
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex ]
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq ]
def initialize(op, left, right) #:nodoc:
unless FilterTypes.include?(op)
raise Net::LDAP::LdapError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter."
raise Net::LDAP::OperatorError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter."
end
@op = op
@left = left
@ -65,6 +65,23 @@ class Net::LDAP::Filter
new(:eq, attribute, value)
end
##
# Creates a Filter object indicating a binary comparison.
# this prevents the search data from being forced into a UTF-8 string.
#
# This is primarily used for Microsoft Active Directory to compare
# GUID values.
#
# # for guid represented as hex charecters
# guid = "6a31b4a12aa27a41aca9603f27dd5116"
# guid_bin = [guid].pack("H*")
# f = Net::LDAP::Filter.bineq("objectGUID", guid_bin)
#
# This filter does not perform any escaping.
def bineq(attribute, value)
new(:bineq, attribute, value)
end
##
# Creates a Filter object indicating extensible comparison. This Filter
# object is currently considered EXPERIMENTAL.
@ -273,7 +290,7 @@ class Net::LDAP::Filter
ber.last.each { |b|
case b.ber_identifier
when 0x80 # context-specific primitive 0, SubstringFilter "initial"
raise Net::LDAP::LdapError, "Unrecognized substring filter; bad initial value." if str.length > 0
raise Net::LDAP::SubstringFilterError, "Unrecognized substring filter; bad initial value." if str.length > 0
str += b
when 0x81 # context-specific primitive 0, SubstringFilter "any"
str += "*#{b}"
@ -292,7 +309,7 @@ class Net::LDAP::Filter
# call to_s to get rid of the BER-identifiedness of the incoming string.
present?(ber.to_s)
when 0xa9 # context-specific constructed 9, "extensible comparison"
raise Net::LDAP::LdapError, "Invalid extensible search filter, should be at least two elements" if ber.size<2
raise Net::LDAP::SearchFilterError, "Invalid extensible search filter, should be at least two elements" if ber.size<2
# Reassembles the extensible filter parts
# (["sn", "2.4.6.8.10", "Barbara Jones", '1'])
@ -313,7 +330,7 @@ class Net::LDAP::Filter
ex(attribute, value)
else
raise Net::LDAP::LdapError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter."
raise Net::LDAP::BERInvalidError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter."
end
end
@ -340,7 +357,7 @@ class Net::LDAP::Filter
when 0xa3 # equalityMatch. context-specific constructed 3.
eq(obj[0], obj[1])
else
raise Net::LDAP::LdapError, "Unknown LDAP search-filter type: #{obj.ber_identifier}"
raise Net::LDAP::SearchFilterTypeUnknownError, "Unknown LDAP search-filter type: #{obj.ber_identifier}"
end
end
end
@ -399,6 +416,8 @@ class Net::LDAP::Filter
"!(#{@left}=#{@right})"
when :eq
"#{@left}=#{@right}"
when :bineq
"#{@left}=#{@right}"
when :ex
"#{@left}:=#{@right}"
when :ge
@ -508,11 +527,14 @@ class Net::LDAP::Filter
else # equality
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3)
end
when :bineq
# make sure data is not forced to UTF-8
[@left.to_s.to_ber, unescape(@right).to_ber_bin].to_ber_contextspecific(3)
when :ex
seq = []
unless @left =~ /^([-;\w]*)(:dn)?(:(\w+|[.\w]+))?$/
raise Net::LDAP::LdapError, "Bad attribute #{@left}"
raise Net::LDAP::BadAttributeError, "Bad attribute #{@left}"
end
type, dn, rule = $1, $2, $4
@ -619,7 +641,7 @@ class Net::LDAP::Filter
l = entry[@left] and l = Array(l) and l.index(@right)
end
else
raise Net::LDAP::LdapError, "Unknown filter type in match: #{@op}"
raise Net::LDAP::FilterTypeUnknownError, "Unknown filter type in match: #{@op}"
end
end
@ -652,7 +674,7 @@ class Net::LDAP::Filter
def initialize(str)
require 'strscan' # Don't load strscan until we need it.
@filter = parse(StringScanner.new(str))
raise Net::LDAP::LdapError, "Invalid filter syntax." unless @filter
raise Net::LDAP::FilterSyntaxInvalidError, "Invalid filter syntax." unless @filter
end
##
@ -733,7 +755,7 @@ class Net::LDAP::Filter
scanner.scan(/\s*/)
if op = scanner.scan(/<=|>=|!=|:=|=/)
scanner.scan(/\s*/)
if value = scanner.scan(/(?:[-\w*.+@=,#\$%&!'\s]|\\[a-fA-F\d]{2})+/)
if value = scanner.scan(/(?:[-\w*.+@=,#\$%&!'\s\xC3\x80-\xCA\xAF]|\\[a-fA-F\d]{2})+/)
# 20100313 AZ: Assumes that "(uid=george*)" is the same as
# "(uid=george* )". The standard doesn't specify, but I can find
# no examples that suggest otherwise.

View File

@ -1,31 +1,37 @@
# -*- ruby encoding: utf-8 -*-
require 'digest/sha1'
require 'digest/md5'
require 'base64'
class Net::LDAP::Password
class << self
# Generate a password-hash suitable for inclusion in an LDAP attribute.
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
# Pass a hash type as a symbol (:md5, :sha, :ssha) and a plaintext
# password. This function will return a hashed representation.
#
#--
# STUB: This is here to fulfill the requirements of an RFC, which
# one?
#
# TODO, gotta do salted-sha and (maybe)salted-md5. Should we provide
# sha1 as a synonym for sha1? I vote no because then should you also
# provide ssha1 for symmetry?
# TODO:
# * maybe salted-md5
# * Should we provide sha1 as a synonym for sha1? I vote no because then
# should you also provide ssha1 for symmetry?
#
attribute_value = ""
def generate(type, str)
digest, digest_name = case type
when :md5
[Digest::MD5.new, 'MD5']
when :sha
[Digest::SHA1.new, 'SHA']
else
raise Net::LDAP::LdapError, "Unsupported password-hash type (#{type})"
end
digest << str.to_s
return "{#{digest_name}}#{[digest.digest].pack('m').chomp }"
case type
when :md5
attribute_value = '{MD5}' + Base64.encode64(Digest::MD5.digest(str)).chomp!
when :sha
attribute_value = '{SHA}' + Base64.encode64(Digest::SHA1.digest(str)).chomp!
when :ssha
srand; salt = (rand * 1000).to_i.to_s
attribute_value = '{SSHA}' + Base64.encode64(Digest::SHA1.digest(str + salt) + salt).chomp!
else
raise Net::LDAP::HashTypeUnsupportedError, "Unsupported password-hash type (#{type})"
end
return attribute_value
end
end
end

View File

@ -112,6 +112,10 @@ class Net::LDAP::PDU
@ldap_result || {}
end
def error_message
result[:errorMessage] || ""
end
##
# This returns an LDAP result code taken from the PDU, but it will be nil
# if there wasn't a result code. That can easily happen depending on the
@ -120,6 +124,18 @@ class Net::LDAP::PDU
@ldap_result and @ldap_result[code]
end
def status
result_code == 0 ? :success : :failure
end
def success?
status == :success
end
def failure?
!success?
end
##
# Return serverSaslCreds, which are only present in BindResponse packets.
#--
@ -136,6 +152,7 @@ class Net::LDAP::PDU
:matchedDN => sequence[1],
:errorMessage => sequence[2]
}
parse_search_referral(sequence[3]) if @ldap_result[:resultCode] == 10
end
private :parse_ldap_result

View File

@ -2,7 +2,7 @@
# :stopdoc:
module Net
class SNMP
VERSION = '0.2.2'
VERSION = '0.4.0'
AsnSyntax = Net::BER.compile_syntax({
:application => {

View File

@ -1,12 +1,11 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{net-ldap}
s.version = "0.2.20110317223538"
s.version = "0.4.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Francis Cianfrocca", "Emiel van de Laar", "Rory O'Connell", "Kaspar Schiess", "Austin Ziegler"]
s.date = %q{2011-03-17}
s.date = %q{2012-02-28}
s.description = %q{Net::LDAP for Ruby (also called net-ldap) implements client access for the
Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for
accessing distributed directory services. Net::LDAP is written completely in
@ -20,8 +19,8 @@ Our roadmap for Net::LDAP 1.0 is to gain full <em>client</em> compliance with
the most recent LDAP RFCs (45104519, plutions of 45204532).}
s.email = ["blackhedd@rubyforge.org", "gemiel@gmail.com", "rory.ocon@gmail.com", "kaspar.schiess@absurd.li", "austin@rubyforge.org"]
s.extra_rdoc_files = ["Manifest.txt", "Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "README.rdoc"]
s.files = [".autotest", ".rspec", "Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "Manifest.txt", "README.rdoc", "Rakefile", "autotest/discover.rb", "lib/net-ldap.rb", "lib/net/ber.rb", "lib/net/ber/ber_parser.rb", "lib/net/ber/core_ext.rb", "lib/net/ber/core_ext/array.rb", "lib/net/ber/core_ext/bignum.rb", "lib/net/ber/core_ext/false_class.rb", "lib/net/ber/core_ext/fixnum.rb", "lib/net/ber/core_ext/string.rb", "lib/net/ber/core_ext/true_class.rb", "lib/net/ldap.rb", "lib/net/ldap/dataset.rb", "lib/net/ldap/dn.rb", "lib/net/ldap/entry.rb", "lib/net/ldap/filter.rb", "lib/net/ldap/password.rb", "lib/net/ldap/pdu.rb", "lib/net/snmp.rb", "net-ldap.gemspec", "spec/integration/ssl_ber_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/unit/ber/ber_spec.rb", "spec/unit/ber/core_ext/string_spec.rb", "spec/unit/ldap/dn_spec.rb", "spec/unit/ldap/entry_spec.rb", "spec/unit/ldap/filter_spec.rb", "spec/unit/ldap_spec.rb", "test/common.rb", "test/test_entry.rb", "test/test_filter.rb", "test/test_ldap_connection.rb", "test/test_ldif.rb", "test/test_password.rb", "test/test_rename.rb", "test/test_snmp.rb", "test/testdata.ldif", "testserver/ldapserver.rb", "testserver/testdata.ldif", ".gemtest"]
s.homepage = %q{http://net-ldap.rubyforge.org/}
s.files = [".autotest", ".rspec", "Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "Manifest.txt", "README.rdoc", "Rakefile", "autotest/discover.rb", "lib/net-ldap.rb", "lib/net/ber.rb", "lib/net/ber/ber_parser.rb", "lib/net/ber/core_ext.rb", "lib/net/ber/core_ext/array.rb", "lib/net/ber/core_ext/bignum.rb", "lib/net/ber/core_ext/false_class.rb", "lib/net/ber/core_ext/fixnum.rb", "lib/net/ber/core_ext/string.rb", "lib/net/ber/core_ext/true_class.rb", "lib/net/ldap.rb", "lib/net/ldap/dataset.rb", "lib/net/ldap/dn.rb", "lib/net/ldap/entry.rb", "lib/net/ldap/filter.rb", "lib/net/ldap/password.rb", "lib/net/ldap/pdu.rb", "lib/net/snmp.rb", "net-ldap.gemspec", "spec/integration/ssl_ber_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/unit/ber/ber_spec.rb", "spec/unit/ber/core_ext/string_spec.rb", "spec/unit/ldap/dn_spec.rb", "spec/unit/ldap/entry_spec.rb", "spec/unit/ldap/filter_spec.rb", "spec/unit/ldap_spec.rb", "test/common.rb", "test/test_entry.rb", "test/test_filter.rb", "test/test_ldap_connection.rb", "test/test_ldif.rb", "test/test_password.rb", "test/test_rename.rb", "test/test_snmp.rb", "test/testdata.ldif", "testserver/ldapserver.rb", "testserver/testdata.ldif"]
s.homepage = %q{http://github.com.org/ruby-ldap/ruby-net-ldap}
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")

View File

@ -75,6 +75,26 @@ describe "BER encoding of" do
end
end
end
if "Ruby 1.9".respond_to?(:encoding)
context "strings" do
it "should properly encode UTF-8 strings" do
"\u00e5".force_encoding("UTF-8").to_ber.should ==
"\x04\x02\xC3\xA5"
end
it "should properly encode strings encodable as UTF-8" do
"teststring".encode("US-ASCII").to_ber.should == "\x04\nteststring"
end
it "should properly encode binary data strings using to_ber_bin" do
# This is used for searching for GUIDs in Active Directory
["6a31b4a12aa27a41aca9603f27dd5116"].pack("H*").to_ber_bin.should ==
"\x04\x10" + "j1\xB4\xA1*\xA2zA\xAC\xA9`?'\xDDQ\x16"
end
it "should fail on strings that can not be converted to UTF-8" do
error = Encoding::UndefinedConversionError
lambda {"\x81".to_ber }.should raise_exception(error)
end
end
end
end
describe "BER decoding of" do
@ -91,4 +111,4 @@ describe "BER decoding of" do
[1, [3, "Administrator", "ad_is_bogus"]]
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'spec_helper'
require 'metaid'
describe Array, "when extended with BER core extensions" do
it "should correctly convert a control code array" do
control_codes = []
control_codes << ['1.2.3'.to_ber, true.to_ber].to_ber_sequence
control_codes << ['1.7.9'.to_ber, false.to_ber].to_ber_sequence
control_codes = control_codes.to_ber_sequence
res = [['1.2.3', true],['1.7.9',false]].to_ber_control
res.should eq(control_codes)
end
it "should wrap the array in another array if a nested array is not passed" do
result1 = ['1.2.3', true].to_ber_control
result2 = [['1.2.3', true]].to_ber_control
result1.should eq(result2)
end
it "should return an empty string if an empty array is passed" do
[].to_ber_control.should be_empty
end
end

View File

@ -0,0 +1,35 @@
# -*- ruby encoding: utf-8 -*-
describe Net::LDAP, "search method" do
class FakeConnection
def search(args)
OpenStruct.new(:result_code => 1, :message => "error")
end
end
before(:each) do
@connection = Net::LDAP.new
@connection.instance_variable_set(:@open_connection, FakeConnection.new)
end
context "when :return_result => true" do
it "should return nil upon error" do
result_set = @connection.search(:return_result => true)
result_set.should be_nil
end
end
context "when :return_result => false" do
it "should return false upon error" do
result = @connection.search(:return_result => false)
result.result_code.should == 1
end
end
context "When :return_result is not given" do
it "should return nil upon error" do
result_set = @connection.search
result_set.should be_nil
end
end
end

View File

@ -7,11 +7,11 @@ describe Net::LDAP::Connection do
flexmock(TCPSocket).
should_receive(:new).and_raise(Errno::ECONNREFUSED)
end
it "should raise LdapError" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(Net::LDAP::LdapError)
end
@ -21,11 +21,11 @@ describe Net::LDAP::Connection do
flexmock(TCPSocket).
should_receive(:new).and_raise(SocketError)
end
it "should raise LdapError" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(Net::LDAP::LdapError)
end
@ -35,14 +35,44 @@ describe Net::LDAP::Connection do
flexmock(TCPSocket).
should_receive(:new).and_raise(NameError)
end
it "should rethrow the exception" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(NameError)
end
end
end
end
context "populate error messages" do
before do
@tcp_socket = flexmock(:connection)
@tcp_socket.should_receive(:write)
flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
end
subject { Net::LDAP::Connection.new(:server => 'test.mocked.com', :port => 636) }
it "should get back error messages if operation fails" do
ber = Net::BER::BerIdentifiedArray.new([53, "", "The provided password value was rejected by a password validator: The provided password did not contain enough characters from the character set 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. The minimum number of characters from that set that must be present in user passwords is 1"])
ber.ber_identifier = 7
@tcp_socket.should_receive(:read_ber).and_return([2, ber])
result = subject.modify(:dn => "1", :operations => [[:replace, "mail", "something@sothsdkf.com"]])
result.should be_failure
result.error_message.should == "The provided password value was rejected by a password validator: The provided password did not contain enough characters from the character set 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. The minimum number of characters from that set that must be present in user passwords is 1"
end
it "shouldn't get back error messages if operation succeeds" do
ber = Net::BER::BerIdentifiedArray.new([0, "", ""])
ber.ber_identifier = 7
@tcp_socket.should_receive(:read_ber).and_return([2, ber])
result = subject.modify(:dn => "1", :operations => [[:replace, "mail", "something@sothsdkf.com"]])
result.should be_success
result.error_message.should == ""
end
end
end

View File

@ -9,11 +9,11 @@ class TestFilter < Test::Unit::TestCase
end
def test_invalid_filter_string
assert_raises(Net::LDAP::LdapError) { Filter.from_rfc2254("") }
assert_raises(Net::LDAP::FilterSyntaxInvalidError) { Filter.from_rfc2254("") }
end
def test_invalid_filter
assert_raises(Net::LDAP::LdapError) {
assert_raises(Net::LDAP::OperatorError) {
# This test exists to prove that our constructor blocks unknown filter
# types. All filters must be constructed using helpers.
Filter.__send__(:new, :xx, nil, nil)
@ -31,92 +31,92 @@ class TestFilter < Test::Unit::TestCase
assert_equal("(uid=*\\5C*)", Filter.contains("uid", "\\").to_s)
end
def test_c2
def test_c2
assert_equal("(uid=george *)",
Filter.from_rfc2254("uid=george *").to_rfc2254)
assert_equal("(uid:=george *)",
Filter.from_rfc2254("uid:=george *").to_rfc2254)
assert_equal("(uid=george*)",
Filter.from_rfc2254(" ( uid = george* ) ").to_rfc2254)
assert_equal("(!(uid=george*))",
assert_equal("(!(uid=george*))",
Filter.from_rfc2254("uid!=george*").to_rfc2254)
assert_equal("(uid<=george*)",
assert_equal("(uid<=george*)",
Filter.from_rfc2254("uid <= george*").to_rfc2254)
assert_equal("(uid>=george*)",
assert_equal("(uid>=george*)",
Filter.from_rfc2254("uid>=george*").to_rfc2254)
assert_equal("(&(uid=george*)(mail=*))",
assert_equal("(&(uid=george*)(mail=*))",
Filter.from_rfc2254("(& (uid=george* ) (mail=*))").to_rfc2254)
assert_equal("(|(uid=george*)(mail=*))",
assert_equal("(|(uid=george*)(mail=*))",
Filter.from_rfc2254("(| (uid=george* ) (mail=*))").to_rfc2254)
assert_equal("(!(mail=*))",
assert_equal("(!(mail=*))",
Filter.from_rfc2254("(! (mail=*))").to_rfc2254)
end
end
def test_filter_with_single_clause
assert_equal("(cn=name)", Net::LDAP::Filter.construct("(&(cn=name))").to_s)
def test_filter_with_single_clause
assert_equal("(cn=name)", Net::LDAP::Filter.construct("(&(cn=name))").to_s)
end
def test_filters_from_ber
[
Net::LDAP::Filter.eq("objectclass", "*"),
Net::LDAP::Filter.pres("objectclass"),
Net::LDAP::Filter.eq("objectclass", "ou"),
Net::LDAP::Filter.ge("uid", "500"),
Net::LDAP::Filter.le("uid", "500"),
(~ Net::LDAP::Filter.pres("objectclass")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou") & Net::LDAP::Filter.pres("sn")),
(Net::LDAP::Filter.pres("objectclass") | Net::LDAP::Filter.pres("ou") | Net::LDAP::Filter.pres("sn")),
Net::LDAP::Filter.eq("objectclass", "*aaa"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "abc*def*1111*22*g"),
Net::LDAP::Filter.eq("objectclass", "*aaa*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc*"),
Net::LDAP::Filter.eq("objectclass", "aaa*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
def test_filters_from_ber
[
Net::LDAP::Filter.eq("objectclass", "*"),
Net::LDAP::Filter.pres("objectclass"),
Net::LDAP::Filter.eq("objectclass", "ou"),
Net::LDAP::Filter.ge("uid", "500"),
Net::LDAP::Filter.le("uid", "500"),
(~ Net::LDAP::Filter.pres("objectclass")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou") & Net::LDAP::Filter.pres("sn")),
(Net::LDAP::Filter.pres("objectclass") | Net::LDAP::Filter.pres("ou") | Net::LDAP::Filter.pres("sn")),
def test_ber_from_rfc2254_filter
[
Net::LDAP::Filter.construct("objectclass=*"),
Net::LDAP::Filter.construct("objectclass=ou"),
Net::LDAP::Filter.construct("uid >= 500"),
Net::LDAP::Filter.construct("uid <= 500"),
Net::LDAP::Filter.construct("(!(uid=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.eq("objectclass", "*aaa"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "abc*def*1111*22*g"),
Net::LDAP::Filter.eq("objectclass", "*aaa*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc*"),
Net::LDAP::Filter.eq("objectclass", "aaa*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
def test_ber_from_rfc2254_filter
[
Net::LDAP::Filter.construct("objectclass=*"),
Net::LDAP::Filter.construct("objectclass=ou"),
Net::LDAP::Filter.construct("uid >= 500"),
Net::LDAP::Filter.construct("uid <= 500"),
Net::LDAP::Filter.construct("(!(uid=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.construct("objectclass=*aaa"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=abc*def*1111*22*g"),
Net::LDAP::Filter.construct("objectclass=*aaa*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc*"),
Net::LDAP::Filter.construct("objectclass=aaa*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
Net::LDAP::Filter.construct("objectclass=*aaa"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=abc*def*1111*22*g"),
Net::LDAP::Filter.construct("objectclass=*aaa*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc*"),
Net::LDAP::Filter.construct("objectclass=aaa*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
end

View File

@ -31,8 +31,20 @@ class TestLdif < Test::Unit::TestCase
end
def test_ldif_with_continuation_lines
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
assert_equal(true, ds.has_key?("abcdefg hijklmn"))
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
assert_equal(true, ds.has_key?("abcdefghijklmn"))
end
def test_ldif_with_continuation_lines_and_extra_whitespace
ds1 = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
assert_equal(true, ds1.has_key?("abcdefg hijklmn"))
ds2 = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hij klmn\r\n\r\n"))
assert_equal(true, ds2.has_key?("abcdefghij klmn"))
end
def test_ldif_tab_is_not_continuation
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: key\r\n\tnotcontinued\r\n\r\n"))
assert_equal(true, ds.has_key?("key"))
end
# TODO, INADEQUATE. We need some more tests