class Net::LDAP::PDU

Defines the Protocol Data Unit (PDU) for LDAP. An LDAP PDU always looks like a BER SEQUENCE with at least two elements: an INTEGER message ID number and an application-specific SEQUENCE. Some LDAPv3 packets also include an optional third element, a sequence of “controls” (see RFC 2251 section 4.1.12 for more information).

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.

Currently, we only support controls on SearchResult.

tools.ietf.org/html/rfc4511#section-4.1.1 tools.ietf.org/html/rfc4511#section-4.1.9

Constants

AbandonRequest

tools.ietf.org/html/rfc4511#section-4.11

AddRequest

tools.ietf.org/html/rfc4511#section-4.7

AddResponse
BindRequest

tools.ietf.org/html/rfc4511#section-4.2

BindResult

tools.ietf.org/html/rfc4511#section-4.2.2

CompareRequest

tools.ietf.org/html/rfc4511#section-4.10

CompareResponse
DeleteRequest

tools.ietf.org/html/rfc4511#section-4.8

DeleteResponse
ExtendedRequest

tools.ietf.org/html/rfc4511#section-4.12

ExtendedResponse
IntermediateResponse

unused: tools.ietf.org/html/rfc4511#section-4.13

ModifyRDNRequest

tools.ietf.org/html/rfc4511#section-4.9

ModifyRDNResponse
ModifyRequest

see also SearchResultReferral (19) tools.ietf.org/html/rfc4511#section-4.6

ModifyResponse
SearchRequest

tools.ietf.org/html/rfc4511#section-4.5.1

SearchResult
SearchResultReferral

tools.ietf.org/html/rfc4511#section-4.5.2

SearchReturnedData

tools.ietf.org/html/rfc4511#section-4.5.2

UnbindRequest

tools.ietf.org/html/rfc4511#section-4.3

Attributes

app_tag[R]

The application protocol format tag.

bind_parameters[R]
extended_response[R]
ldap_controls[R]

Returns RFC-2251 Controls if any.

message_id[R]

The LDAP packet message ID.

msg_id[R]

The LDAP packet message ID.

result_controls[R]

Returns RFC-2251 Controls if any.

search_entry[R]
search_parameters[R]
search_referrals[R]

Public Class Methods

new(ber_object) click to toggle source

Messy. Does this functionality belong somewhere else?

# File lib/net/ldap/pdu.rb, line 84
def initialize(ber_object)
  begin
    @message_id = ber_object[0].to_i
    # Grab the bottom five bits of the identifier so we know which type of
    # PDU this is.
    #
    # This is safe enough in LDAP-land, but it is recommended that other
    # approaches be taken for other protocols in the case that there's an
    # app-specific tag that has both primitive and constructed forms.
    @app_tag = ber_object[1].ber_identifier & 0x1f
    @ldap_controls = []
  rescue Exception => ex
    raise Net::LDAP::PDU::Error, "LDAP PDU Format Error: #{ex.message}"
  end

  case @app_tag
  when BindResult
    parse_bind_response(ber_object[1])
  when SearchReturnedData
    parse_search_return(ber_object[1])
  when SearchResultReferral
    parse_search_referral(ber_object[1])
  when SearchResult
    parse_ldap_result(ber_object[1])
  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_extended_response(ber_object[1])
  else
    raise LdapPduError.new("unknown pdu-type: #{@app_tag}")
  end

  parse_controls(ber_object[2]) if ber_object[2]
end

Public Instance Methods

error_message() click to toggle source
# File lib/net/ldap/pdu.rb, line 139
def error_message
  result[:errorMessage] || ""
end
failure?() click to toggle source
# File lib/net/ldap/pdu.rb, line 159
def failure?
  !success?
end
result() click to toggle source

Returns a hash which (usually) defines the members :resultCode, :errorMessage, and :matchedDN. These values come directly from an LDAP response packet returned by the remote peer. Also see result_code.

# File lib/net/ldap/pdu.rb, line 135
def result
  @ldap_result || {}
end
result_code(code = :resultCode) click to toggle source

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 type of packet.

# File lib/net/ldap/pdu.rb, line 147
def result_code(code = :resultCode)
  @ldap_result and @ldap_result[code]
end
result_server_sasl_creds() click to toggle source

Return serverSaslCreds, which are only present in BindResponse packets.

# File lib/net/ldap/pdu.rb, line 168
def result_server_sasl_creds
  @ldap_result && @ldap_result[:serverSaslCreds]
end
status() click to toggle source
# File lib/net/ldap/pdu.rb, line 151
def status
  Net::LDAP::ResultCodesNonError.include?(result_code) ? :success : :failure
end
success?() click to toggle source
# File lib/net/ldap/pdu.rb, line 155
def success?
  status == :success
end

Private Instance Methods

parse_bind_request(sequence) click to toggle source

(provisional, must document)

# File lib/net/ldap/pdu.rb, line 288
def parse_bind_request sequence
  s = OpenStruct.new
  s.version, s.name, s.authentication = sequence
  @bind_parameters = s
end
parse_bind_response(sequence) click to toggle source

A Bind Response may have an additional field, ID [7], serverSaslCreds, per RFC 2251 pgh 4.2.3.

# File lib/net/ldap/pdu.rb, line 209
def parse_bind_response(sequence)
  sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP Bind Response length."
  parse_ldap_result(sequence)
  @ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
  @ldap_result
end
parse_controls(sequence) click to toggle source

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.

# File lib/net/ldap/pdu.rb, line 265
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
parse_extended_response(sequence) click to toggle source

Parse an extended response

www.ietf.org/rfc/rfc2251.txt

Each Extended operation consists of an Extended request and an Extended response.

ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
     requestName      [0] LDAPOID,
     requestValue     [1] OCTET STRING OPTIONAL }
# File lib/net/ldap/pdu.rb, line 195
def parse_extended_response(sequence)
  sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
  @ldap_result = {
    :resultCode => sequence[0],
    :matchedDN => sequence[1],
    :errorMessage => sequence[2],
  }
  @extended_response = sequence[3]
end
parse_ldap_result(sequence) click to toggle source
# File lib/net/ldap/pdu.rb, line 172
def parse_ldap_result(sequence)
  sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
  @ldap_result = {
    :resultCode => sequence[0],
    :matchedDN => sequence[1],
    :errorMessage => sequence[2],
  }
  parse_search_referral(sequence[3]) if @ldap_result[:resultCode] == Net::LDAP::ResultCodeReferral
end
parse_ldap_search_request(sequence) click to toggle source

(provisional, must document)

# File lib/net/ldap/pdu.rb, line 279
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
parse_search_referral(uris) click to toggle source

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 with normal replies in any order.

# File lib/net/ldap/pdu.rb, line 253
def parse_search_referral(uris)
  @search_referrals = uris
end
parse_search_return(sequence) click to toggle source

Definition from RFC 1777 (we're handling application-4 here).

Search Response ::=

CHOICE {
  entry      [APPLICATION 4] SEQUENCE {
               objectName     LDAPDN,
               attributes     SEQUENCE OF SEQUENCE {
                 AttributeType,
                 SET OF AttributeValue
               }
             },
  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.

This is to make them more predictable for user programs, but it may not be a good idea. Maybe this should be configurable.

# File lib/net/ldap/pdu.rb, line 238
def parse_search_return(sequence)
  sequence.length >= 2 or raise Net::LDAP::PDU::Error, "Invalid Search Response length."
  @search_entry = Net::LDAP::Entry.new(sequence[0])
  sequence[1].each { |seq| @search_entry[seq[0]] = seq[1] }
end
parse_unbind_request(sequence) click to toggle source

(provisional, must document) UnbindRequest has no content so this is a no-op.

# File lib/net/ldap/pdu.rb, line 297
def parse_unbind_request(sequence)
  nil
end