To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / .svn / pristine / 60 / 60a7eaf3b71f4ea5a952f91807abdd540583522f.svn-base @ 1297:0a574315af3e
History | View | Annotate | Download (6.53 KB)
| 1 |
# $Id: pdu.rb 126 2006-05-31 15:55:16Z blackhedd $ |
|---|---|
| 2 |
# |
| 3 |
# LDAP PDU support classes |
| 4 |
# |
| 5 |
# |
| 6 |
#---------------------------------------------------------------------------- |
| 7 |
# |
| 8 |
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved. |
| 9 |
# |
| 10 |
# Gmail: garbagecat10 |
| 11 |
# |
| 12 |
# This program is free software; you can redistribute it and/or modify |
| 13 |
# it under the terms of the GNU General Public License as published by |
| 14 |
# the Free Software Foundation; either version 2 of the License, or |
| 15 |
# (at your option) any later version. |
| 16 |
# |
| 17 |
# This program is distributed in the hope that it will be useful, |
| 18 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 |
# GNU General Public License for more details. |
| 21 |
# |
| 22 |
# You should have received a copy of the GNU General Public License |
| 23 |
# along with this program; if not, write to the Free Software |
| 24 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 25 |
# |
| 26 |
#--------------------------------------------------------------------------- |
| 27 |
# |
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
module Net |
| 32 |
|
| 33 |
|
| 34 |
class LdapPduError < Exception; end |
| 35 |
|
| 36 |
|
| 37 |
class LdapPdu |
| 38 |
|
| 39 |
BindResult = 1 |
| 40 |
SearchReturnedData = 4 |
| 41 |
SearchResult = 5 |
| 42 |
ModifyResponse = 7 |
| 43 |
AddResponse = 9 |
| 44 |
DeleteResponse = 11 |
| 45 |
ModifyRDNResponse = 13 |
| 46 |
SearchResultReferral = 19 |
| 47 |
|
| 48 |
attr_reader :msg_id, :app_tag |
| 49 |
attr_reader :search_dn, :search_attributes, :search_entry |
| 50 |
attr_reader :search_referrals |
| 51 |
|
| 52 |
# |
| 53 |
# initialize |
| 54 |
# An LDAP PDU always looks like a BerSequence with |
| 55 |
# at least two elements: an integer (message-id number), and |
| 56 |
# an application-specific sequence. |
| 57 |
# Some LDAPv3 packets also include an optional |
| 58 |
# third element, which is a sequence of "controls" |
| 59 |
# (See RFC 2251, section 4.1.12). |
| 60 |
# The application-specific tag in the sequence tells |
| 61 |
# us what kind of packet it is, and each kind has its |
| 62 |
# own format, defined in RFC-1777. |
| 63 |
# Observe that many clients (such as ldapsearch) |
| 64 |
# do not necessarily enforce the expected application |
| 65 |
# tags on received protocol packets. This implementation |
| 66 |
# does interpret the RFC strictly in this regard, and |
| 67 |
# it remains to be seen whether there are servers out |
| 68 |
# there that will not work well with our approach. |
| 69 |
# |
| 70 |
# Added a controls-processor to SearchResult. |
| 71 |
# Didn't add it everywhere because it just _feels_ |
| 72 |
# like it will need to be refactored. |
| 73 |
# |
| 74 |
def initialize ber_object |
| 75 |
begin |
| 76 |
@msg_id = ber_object[0].to_i |
| 77 |
@app_tag = ber_object[1].ber_identifier - 0x60 |
| 78 |
rescue |
| 79 |
# any error becomes a data-format error |
| 80 |
raise LdapPduError.new( "ldap-pdu format error" ) |
| 81 |
end |
| 82 |
|
| 83 |
case @app_tag |
| 84 |
when BindResult |
| 85 |
parse_ldap_result ber_object[1] |
| 86 |
when SearchReturnedData |
| 87 |
parse_search_return ber_object[1] |
| 88 |
when SearchResultReferral |
| 89 |
parse_search_referral ber_object[1] |
| 90 |
when SearchResult |
| 91 |
parse_ldap_result ber_object[1] |
| 92 |
parse_controls(ber_object[2]) if ber_object[2] |
| 93 |
when ModifyResponse |
| 94 |
parse_ldap_result ber_object[1] |
| 95 |
when AddResponse |
| 96 |
parse_ldap_result ber_object[1] |
| 97 |
when DeleteResponse |
| 98 |
parse_ldap_result ber_object[1] |
| 99 |
when ModifyRDNResponse |
| 100 |
parse_ldap_result ber_object[1] |
| 101 |
else |
| 102 |
raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
|
| 103 |
end |
| 104 |
end |
| 105 |
|
| 106 |
# |
| 107 |
# result_code |
| 108 |
# This returns an LDAP result code taken from the PDU, |
| 109 |
# but it will be nil if there wasn't a result code. |
| 110 |
# That can easily happen depending on the type of packet. |
| 111 |
# |
| 112 |
def result_code code = :resultCode |
| 113 |
@ldap_result and @ldap_result[code] |
| 114 |
end |
| 115 |
|
| 116 |
# Return RFC-2251 Controls if any. |
| 117 |
# Messy. Does this functionality belong somewhere else? |
| 118 |
def result_controls |
| 119 |
@ldap_controls || [] |
| 120 |
end |
| 121 |
|
| 122 |
|
| 123 |
# |
| 124 |
# parse_ldap_result |
| 125 |
# |
| 126 |
def parse_ldap_result sequence |
| 127 |
sequence.length >= 3 or raise LdapPduError |
| 128 |
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
| 129 |
end |
| 130 |
private :parse_ldap_result |
| 131 |
|
| 132 |
# |
| 133 |
# parse_search_return |
| 134 |
# Definition from RFC 1777 (we're handling application-4 here) |
| 135 |
# |
| 136 |
# Search Response ::= |
| 137 |
# CHOICE {
|
| 138 |
# entry [APPLICATION 4] SEQUENCE {
|
| 139 |
# objectName LDAPDN, |
| 140 |
# attributes SEQUENCE OF SEQUENCE {
|
| 141 |
# AttributeType, |
| 142 |
# SET OF AttributeValue |
| 143 |
# } |
| 144 |
# }, |
| 145 |
# resultCode [APPLICATION 5] LDAPResult |
| 146 |
# } |
| 147 |
# |
| 148 |
# We concoct a search response that is a hash of the returned attribute values. |
| 149 |
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES. |
| 150 |
# This is to make them more predictable for user programs, but it |
| 151 |
# may not be a good idea. Maybe this should be configurable. |
| 152 |
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes, |
| 153 |
# we also return @search_entry, which is an LDAP::Entry object. |
| 154 |
# If that works out well, then we'll remove the first two. |
| 155 |
# |
| 156 |
# Provisionally removed obsolete search_attributes and search_dn, 04May06. |
| 157 |
# |
| 158 |
def parse_search_return sequence |
| 159 |
sequence.length >= 2 or raise LdapPduError |
| 160 |
@search_entry = LDAP::Entry.new( sequence[0] ) |
| 161 |
#@search_dn = sequence[0] |
| 162 |
#@search_attributes = {}
|
| 163 |
sequence[1].each {|seq|
|
| 164 |
@search_entry[seq[0]] = seq[1] |
| 165 |
#@search_attributes[seq[0].downcase.intern] = seq[1] |
| 166 |
} |
| 167 |
end |
| 168 |
|
| 169 |
# |
| 170 |
# A search referral is a sequence of one or more LDAP URIs. |
| 171 |
# Any number of search-referral replies can be returned by the server, interspersed |
| 172 |
# with normal replies in any order. |
| 173 |
# Until I can think of a better way to do this, we'll return the referrals as an array. |
| 174 |
# It'll be up to higher-level handlers to expose something reasonable to the client. |
| 175 |
def parse_search_referral uris |
| 176 |
@search_referrals = uris |
| 177 |
end |
| 178 |
|
| 179 |
|
| 180 |
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting |
| 181 |
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL |
| 182 |
# Octet String. If only two fields are given, the second one may be |
| 183 |
# either criticality or data, since criticality has a default value. |
| 184 |
# Someday we may want to come back here and add support for some of |
| 185 |
# more-widely used controls. RFC-2696 is a good example. |
| 186 |
# |
| 187 |
def parse_controls sequence |
| 188 |
@ldap_controls = sequence.map do |control| |
| 189 |
o = OpenStruct.new |
| 190 |
o.oid,o.criticality,o.value = control[0],control[1],control[2] |
| 191 |
if o.criticality and o.criticality.is_a?(String) |
| 192 |
o.value = o.criticality |
| 193 |
o.criticality = false |
| 194 |
end |
| 195 |
o |
| 196 |
end |
| 197 |
end |
| 198 |
private :parse_controls |
| 199 |
|
| 200 |
|
| 201 |
end |
| 202 |
|
| 203 |
|
| 204 |
end # module Net |
| 205 |
|