summaryrefslogtreecommitdiffstats
path: root/vendor/plugins/ruby-net-ldap-0.0.4/lib/net/ldap/pdu.rb
blob: dbc0d6f10db40e1e77dcf60c1b3f09f5a4c1ed64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# $Id: pdu.rb 126 2006-05-31 15:55:16Z blackhedd $
#
# LDAP PDU support classes
#
#
#----------------------------------------------------------------------------
#
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
#
# Gmail: garbagecat10
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
#---------------------------------------------------------------------------
#



module Net


class LdapPduError < Exception; end


class LdapPdu

  BindResult = 1
  SearchReturnedData = 4
  SearchResult = 5
  ModifyResponse = 7
  AddResponse = 9
  DeleteResponse = 11
  ModifyRDNResponse = 13
  SearchResultReferral = 19

  attr_reader :msg_id, :app_tag
  attr_reader :search_dn, :search_attributes, :search_entry
  attr_reader :search_referrals

  #
  # initialize
  # An LDAP PDU always looks like a BerSequence with
  # at least two elements: an integer (message-id number), and
  # an application-specific sequence.
  # Some LDAPv3 packets also include an optional
  # third element, which is a sequence of "controls"
  # (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
      @app_tag = ber_object[1].ber_identifier - 0x60
    rescue
      # any error becomes a data-format error
      raise LdapPduError.new( "ldap-pdu format error" )
    end

    case @app_tag
    when BindResult
      parse_ldap_result 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]
      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]
    else
      raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
    end
  end

  #
  # result_code
  # 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.
  #
  def result_code code = :resultCode
    @ldap_result and @ldap_result[code]
  end

  # Return RFC-2251 Controls if any.
  # Messy. Does this functionality belong somewhere else?
  def result_controls
    @ldap_controls || []
  end


  #
  # parse_ldap_result
  #
  def parse_ldap_result sequence
    sequence.length >= 3 or raise LdapPduError
    @ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
  end
  private :parse_ldap_result

  #
  # parse_search_return
  # 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.
  # ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
  # 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.
  #
  def parse_search_return sequence
    sequence.length >= 2 or raise LdapPduError
    @search_entry = LDAP::Entry.new( sequence[0] )
    #@search_dn = sequence[0]
    #@search_attributes = {}
    sequence[1].each {|seq|
      @search_entry[seq[0]] = seq[1]
      #@search_attributes[seq[0].downcase.intern] = seq[1]
    }
  end

  #
  # 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.
  # 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.
  def parse_search_referral uris
    @search_referrals = uris
  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
  private :parse_controls


end


end # module Net