Correct indentation and format.
This commit is contained in:
parent
2835ecd82d
commit
ec47390431
|
@ -2,7 +2,6 @@
|
||||||
#
|
#
|
||||||
# LDAP PDU support classes
|
# LDAP PDU support classes
|
||||||
#
|
#
|
||||||
#
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
||||||
|
@ -24,255 +23,236 @@
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#
|
#
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
|
class LdapPduError < StandardError; end
|
||||||
|
|
||||||
class LdapPduError < StandardError; end
|
class LdapPdu
|
||||||
|
|
||||||
|
BindRequest = 0
|
||||||
|
BindResult = 1
|
||||||
|
UnbindRequest = 2
|
||||||
|
SearchRequest = 3
|
||||||
|
SearchReturnedData = 4
|
||||||
|
SearchResult = 5
|
||||||
|
ModifyResponse = 7
|
||||||
|
AddResponse = 9
|
||||||
|
DeleteResponse = 11
|
||||||
|
ModifyRDNResponse = 13
|
||||||
|
SearchResultReferral = 19
|
||||||
|
ExtendedRequest = 23
|
||||||
|
ExtendedResponse = 24
|
||||||
|
|
||||||
class LdapPdu
|
attr_reader :msg_id, :app_tag
|
||||||
|
attr_reader :search_dn, :search_attributes, :search_entry
|
||||||
|
attr_reader :search_referrals
|
||||||
|
attr_reader :search_parameters, :bind_parameters
|
||||||
|
|
||||||
BindRequest = 0
|
# An LDAP PDU always looks like a BerSequence with
|
||||||
BindResult = 1
|
# at least two elements: an integer (message-id number), and
|
||||||
UnbindRequest = 2
|
# an application-specific sequence.
|
||||||
SearchRequest = 3
|
# Some LDAPv3 packets also include an optional
|
||||||
SearchReturnedData = 4
|
# third element, which is a sequence of "controls"
|
||||||
SearchResult = 5
|
# (See RFC 2251, section 4.1.12).
|
||||||
ModifyResponse = 7
|
# The application-specific tag in the sequence tells
|
||||||
AddResponse = 9
|
# us what kind of packet it is, and each kind has its
|
||||||
DeleteResponse = 11
|
# own format, defined in RFC-1777.
|
||||||
ModifyRDNResponse = 13
|
# Observe that many clients (such as ldapsearch)
|
||||||
SearchResultReferral = 19
|
# do not necessarily enforce the expected application
|
||||||
ExtendedRequest = 23
|
# tags on received protocol packets. This implementation
|
||||||
ExtendedResponse = 24
|
# does interpret the RFC strictly in this regard, and
|
||||||
|
# it remains to be seen whether there are servers out
|
||||||
|
# there that will not work well with our approach.
|
||||||
|
#
|
||||||
|
# Added a controls-processor to SearchResult.
|
||||||
|
# Didn't add it everywhere because it just _feels_
|
||||||
|
# like it will need to be refactored.
|
||||||
|
#
|
||||||
|
def initialize ber_object
|
||||||
|
begin
|
||||||
|
@msg_id = ber_object[0].to_i
|
||||||
|
# Modified 25Nov06. We want to "un-decorate" the ber-identifier
|
||||||
|
# of the incoming packet. Originally we did this by subtracting 0x60,
|
||||||
|
# which ASSUMES the identifier is a constructed app-specific value.
|
||||||
|
# But at least one value (UnbindRequest) is app-specific primitive.
|
||||||
|
# So it makes more sense just to grab the bottom five bits.
|
||||||
|
#@app_tag = ber_object[1].ber_identifier - 0x60
|
||||||
|
@app_tag = ber_object[1].ber_identifier & 31
|
||||||
|
@ldap_controls = []
|
||||||
|
rescue
|
||||||
|
# any error becomes a data-format error
|
||||||
|
raise LdapPduError.new( "ldap-pdu format error" )
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :msg_id, :app_tag
|
case @app_tag
|
||||||
attr_reader :search_dn, :search_attributes, :search_entry
|
when BindResult
|
||||||
attr_reader :search_referrals
|
parse_bind_response ber_object[1]
|
||||||
attr_reader :search_parameters, :bind_parameters
|
when SearchReturnedData
|
||||||
|
parse_search_return ber_object[1]
|
||||||
|
when SearchResultReferral
|
||||||
|
parse_search_referral ber_object[1]
|
||||||
|
when SearchResult
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
parse_controls(ber_object[2]) if ber_object[2]
|
||||||
|
when ModifyResponse
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
when AddResponse
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
when DeleteResponse
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
when ModifyRDNResponse
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
when SearchRequest
|
||||||
|
parse_ldap_search_request ber_object[1]
|
||||||
|
when BindRequest
|
||||||
|
parse_bind_request ber_object[1]
|
||||||
|
when UnbindRequest
|
||||||
|
parse_unbind_request ber_object[1]
|
||||||
|
when ExtendedResponse
|
||||||
|
parse_ldap_result ber_object[1]
|
||||||
|
else
|
||||||
|
raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#
|
# Returns a hash which (usually) defines the members :resultCode, :errorMessage, and :matchedDN.
|
||||||
# initialize
|
# These values come directly from an LDAP response packet returned by the remote peer.
|
||||||
# An LDAP PDU always looks like a BerSequence with
|
# See #result_code for a sugaring.
|
||||||
# at least two elements: an integer (message-id number), and
|
#
|
||||||
# an application-specific sequence.
|
def result
|
||||||
# Some LDAPv3 packets also include an optional
|
@ldap_result || {}
|
||||||
# third element, which is a sequence of "controls"
|
end
|
||||||
# (See RFC 2251, section 4.1.12).
|
|
||||||
# The application-specific tag in the sequence tells
|
|
||||||
# us what kind of packet it is, and each kind has its
|
|
||||||
# own format, defined in RFC-1777.
|
|
||||||
# Observe that many clients (such as ldapsearch)
|
|
||||||
# do not necessarily enforce the expected application
|
|
||||||
# tags on received protocol packets. This implementation
|
|
||||||
# does interpret the RFC strictly in this regard, and
|
|
||||||
# it remains to be seen whether there are servers out
|
|
||||||
# there that will not work well with our approach.
|
|
||||||
#
|
|
||||||
# Added a controls-processor to SearchResult.
|
|
||||||
# Didn't add it everywhere because it just _feels_
|
|
||||||
# like it will need to be refactored.
|
|
||||||
#
|
|
||||||
def initialize ber_object
|
|
||||||
begin
|
|
||||||
@msg_id = ber_object[0].to_i
|
|
||||||
# Modified 25Nov06. We want to "un-decorate" the ber-identifier
|
|
||||||
# of the incoming packet. Originally we did this by subtracting 0x60,
|
|
||||||
# which ASSUMES the identifier is a constructed app-specific value.
|
|
||||||
# But at least one value (UnbindRequest) is app-specific primitive.
|
|
||||||
# So it makes more sense just to grab the bottom five bits.
|
|
||||||
#@app_tag = ber_object[1].ber_identifier - 0x60
|
|
||||||
@app_tag = ber_object[1].ber_identifier & 31
|
|
||||||
@ldap_controls = []
|
|
||||||
rescue
|
|
||||||
# any error becomes a data-format error
|
|
||||||
raise LdapPduError.new( "ldap-pdu format error" )
|
|
||||||
end
|
|
||||||
|
|
||||||
case @app_tag
|
# This returns an LDAP result code taken from the PDU,
|
||||||
when BindResult
|
# but it will be nil if there wasn't a result code.
|
||||||
parse_bind_response ber_object[1]
|
# That can easily happen depending on the type of packet.
|
||||||
when SearchReturnedData
|
#
|
||||||
parse_search_return ber_object[1]
|
def result_code code = :resultCode
|
||||||
when SearchResultReferral
|
@ldap_result and @ldap_result[code]
|
||||||
parse_search_referral ber_object[1]
|
end
|
||||||
when SearchResult
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
parse_controls(ber_object[2]) if ber_object[2]
|
|
||||||
when ModifyResponse
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
when AddResponse
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
when DeleteResponse
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
when ModifyRDNResponse
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
when SearchRequest
|
|
||||||
parse_ldap_search_request ber_object[1]
|
|
||||||
when BindRequest
|
|
||||||
parse_bind_request ber_object[1]
|
|
||||||
when UnbindRequest
|
|
||||||
parse_unbind_request ber_object[1]
|
|
||||||
when ExtendedResponse
|
|
||||||
parse_ldap_result ber_object[1]
|
|
||||||
else
|
|
||||||
raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a hash which (usually) defines the members :resultCode, :errorMessage, and :matchedDN.
|
# Return RFC-2251 Controls if any.
|
||||||
# These values come directly from an LDAP response packet returned by the remote peer.
|
# Messy. Does this functionality belong somewhere else?
|
||||||
# See #result_code for a sugaring.
|
def result_controls
|
||||||
#
|
@ldap_controls
|
||||||
def result
|
end
|
||||||
@ldap_result || {}
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
# Return serverSaslCreds, which are only present in BindResponse packets.
|
||||||
# result_code
|
# Messy. Does this functionality belong somewhere else?
|
||||||
# This returns an LDAP result code taken from the PDU,
|
# We ought to refactor the accessors of this class before they get any kludgier.
|
||||||
# but it will be nil if there wasn't a result code.
|
def result_server_sasl_creds
|
||||||
# That can easily happen depending on the type of packet.
|
@ldap_result && @ldap_result[:serverSaslCreds]
|
||||||
#
|
end
|
||||||
def result_code code = :resultCode
|
|
||||||
@ldap_result and @ldap_result[code]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return RFC-2251 Controls if any.
|
# parse_ldap_result
|
||||||
# Messy. Does this functionality belong somewhere else?
|
#
|
||||||
def result_controls
|
def parse_ldap_result sequence
|
||||||
@ldap_controls
|
sequence.length >= 3 or raise LdapPduError
|
||||||
end
|
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
||||||
|
end
|
||||||
|
|
||||||
# Return serverSaslCreds, which are only present in BindResponse packets.
|
private :parse_ldap_result
|
||||||
# Messy. Does this functionality belong somewhere else?
|
|
||||||
# We ought to refactor the accessors of this class before they get any kludgier.
|
|
||||||
def result_server_sasl_creds
|
|
||||||
@ldap_result && @ldap_result[:serverSaslCreds]
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
# A Bind Response may have an additional field, ID [7], serverSaslCreds, per RFC 2251 pgh 4.2.3.
|
||||||
# parse_ldap_result
|
#
|
||||||
#
|
def parse_bind_response sequence
|
||||||
def parse_ldap_result sequence
|
sequence.length >= 3 or raise LdapPduError
|
||||||
sequence.length >= 3 or raise LdapPduError
|
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
||||||
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
@ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
|
||||||
end
|
@ldap_result
|
||||||
private :parse_ldap_result
|
end
|
||||||
|
|
||||||
#
|
private :parse_bind_response
|
||||||
# parse_bind_response
|
|
||||||
# A Bind Response may have an additional field, ID [7], serverSaslCreds, per RFC 2251 pgh 4.2.3.
|
|
||||||
#
|
|
||||||
def parse_bind_response sequence
|
|
||||||
sequence.length >= 3 or raise LdapPduError
|
|
||||||
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
|
||||||
@ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
|
|
||||||
@ldap_result
|
|
||||||
end
|
|
||||||
private :parse_bind_response
|
|
||||||
|
|
||||||
#
|
# Definition from RFC 1777 (we're handling application-4 here)
|
||||||
# parse_search_return
|
#
|
||||||
# Definition from RFC 1777 (we're handling application-4 here)
|
# Search Response ::=
|
||||||
#
|
# CHOICE {
|
||||||
# Search Response ::=
|
# entry [APPLICATION 4] SEQUENCE {
|
||||||
# CHOICE {
|
# objectName LDAPDN,
|
||||||
# entry [APPLICATION 4] SEQUENCE {
|
# attributes SEQUENCE OF SEQUENCE {
|
||||||
# objectName LDAPDN,
|
# AttributeType,
|
||||||
# attributes SEQUENCE OF SEQUENCE {
|
# SET OF AttributeValue
|
||||||
# AttributeType,
|
# }
|
||||||
# SET OF AttributeValue
|
# },
|
||||||
# }
|
# resultCode [APPLICATION 5] LDAPResult
|
||||||
# },
|
# }
|
||||||
# resultCode [APPLICATION 5] LDAPResult
|
#
|
||||||
# }
|
# We concoct a search response that is a hash of the returned attribute values.
|
||||||
#
|
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
||||||
# We concoct a search response that is a hash of the returned attribute values.
|
# This is to make them more predictable for user programs, but it
|
||||||
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
# may not be a good idea. Maybe this should be configurable.
|
||||||
# This is to make them more predictable for user programs, but it
|
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
|
||||||
# may not be a good idea. Maybe this should be configurable.
|
# we also return @search_entry, which is an LDAP::Entry object.
|
||||||
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
|
# If that works out well, then we'll remove the first two.
|
||||||
# we also return @search_entry, which is an LDAP::Entry object.
|
#
|
||||||
# If that works out well, then we'll remove the first two.
|
# Provisionally removed obsolete search_attributes and search_dn, 04May06.
|
||||||
#
|
#
|
||||||
# Provisionally removed obsolete search_attributes and search_dn, 04May06.
|
def parse_search_return sequence
|
||||||
#
|
sequence.length >= 2 or raise LdapPduError
|
||||||
def parse_search_return sequence
|
@search_entry = LDAP::Entry.new( sequence[0] )
|
||||||
sequence.length >= 2 or raise LdapPduError
|
sequence[1].each {|seq|
|
||||||
@search_entry = LDAP::Entry.new( sequence[0] )
|
@search_entry[seq[0]] = seq[1]
|
||||||
sequence[1].each {|seq|
|
}
|
||||||
@search_entry[seq[0]] = seq[1]
|
end
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
# A search referral is a sequence of one or more LDAP URIs.
|
||||||
# A search referral is a sequence of one or more LDAP URIs.
|
# Any number of search-referral replies can be returned by the server, interspersed
|
||||||
# Any number of search-referral replies can be returned by the server, interspersed
|
# with normal replies in any order.
|
||||||
# with normal replies in any order.
|
# Until I can think of a better way to do this, we'll return the referrals as an array.
|
||||||
# Until I can think of a better way to do this, we'll return the referrals as an array.
|
# It'll be up to higher-level handlers to expose something reasonable to the client.
|
||||||
# It'll be up to higher-level handlers to expose something reasonable to the client.
|
def parse_search_referral uris
|
||||||
def parse_search_referral uris
|
@search_referrals = uris
|
||||||
@search_referrals = uris
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
|
||||||
|
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
|
||||||
|
# Octet String. If only two fields are given, the second one may be
|
||||||
|
# either criticality or data, since criticality has a default value.
|
||||||
|
# Someday we may want to come back here and add support for some of
|
||||||
|
# more-widely used controls. RFC-2696 is a good example.
|
||||||
|
#
|
||||||
|
def parse_controls sequence
|
||||||
|
@ldap_controls = sequence.map do |control|
|
||||||
|
o = OpenStruct.new
|
||||||
|
o.oid,o.criticality,o.value = control[0],control[1],control[2]
|
||||||
|
if o.criticality and o.criticality.is_a?(String)
|
||||||
|
o.value = o.criticality
|
||||||
|
o.criticality = false
|
||||||
|
end
|
||||||
|
o
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
|
private :parse_controls
|
||||||
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
|
|
||||||
# Octet String. If only two fields are given, the second one may be
|
|
||||||
# either criticality or data, since criticality has a default value.
|
|
||||||
# Someday we may want to come back here and add support for some of
|
|
||||||
# more-widely used controls. RFC-2696 is a good example.
|
|
||||||
#
|
|
||||||
def parse_controls sequence
|
|
||||||
@ldap_controls = sequence.map do |control|
|
|
||||||
o = OpenStruct.new
|
|
||||||
o.oid,o.criticality,o.value = control[0],control[1],control[2]
|
|
||||||
if o.criticality and o.criticality.is_a?(String)
|
|
||||||
o.value = o.criticality
|
|
||||||
o.criticality = false
|
|
||||||
end
|
|
||||||
o
|
|
||||||
end
|
|
||||||
end
|
|
||||||
private :parse_controls
|
|
||||||
|
|
||||||
|
# (provisional, must document)
|
||||||
|
def parse_ldap_search_request sequence
|
||||||
|
s = OpenStruct.new
|
||||||
|
s.base_object,
|
||||||
|
s.scope,
|
||||||
|
s.deref_aliases,
|
||||||
|
s.size_limit,
|
||||||
|
s.time_limit,
|
||||||
|
s.types_only,
|
||||||
|
s.filter,
|
||||||
|
s.attributes = sequence
|
||||||
|
@search_parameters = s
|
||||||
|
end
|
||||||
|
|
||||||
# (provisional, must document)
|
# (provisional, must document)
|
||||||
def parse_ldap_search_request sequence
|
def parse_bind_request sequence
|
||||||
s = OpenStruct.new
|
s = OpenStruct.new
|
||||||
s.base_object,
|
s.version,
|
||||||
s.scope,
|
s.name,
|
||||||
s.deref_aliases,
|
s.authentication = sequence
|
||||||
s.size_limit,
|
@bind_parameters = s
|
||||||
s.time_limit,
|
end
|
||||||
s.types_only,
|
|
||||||
s.filter,
|
|
||||||
s.attributes = sequence
|
|
||||||
@search_parameters = s
|
|
||||||
end
|
|
||||||
|
|
||||||
# (provisional, must document)
|
|
||||||
def parse_bind_request sequence
|
|
||||||
s = OpenStruct.new
|
|
||||||
s.version,
|
|
||||||
s.name,
|
|
||||||
s.authentication = sequence
|
|
||||||
@bind_parameters = s
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# (provisional, must document)
|
|
||||||
# UnbindRequest has no content so this is a no-op.
|
|
||||||
def parse_unbind_request sequence
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
# (provisional, must document)
|
||||||
|
# UnbindRequest has no content so this is a no-op.
|
||||||
|
def parse_unbind_request sequence
|
||||||
|
end
|
||||||
|
end
|
||||||
end # module Net
|
end # module Net
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue