scashin133-net-ldap 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +272 -0
- data/Hacking.rdoc +16 -0
- data/History.txt +137 -0
- data/LICENSE +56 -0
- data/Manifest.txt +42 -0
- data/README.txt +70 -0
- data/Rakefile +124 -0
- data/lib/net/ber/ber_parser.rb +168 -0
- data/lib/net/ber/core_ext/array.rb +79 -0
- data/lib/net/ber/core_ext/bignum.rb +19 -0
- data/lib/net/ber/core_ext/false_class.rb +7 -0
- data/lib/net/ber/core_ext/fixnum.rb +63 -0
- data/lib/net/ber/core_ext/string.rb +45 -0
- data/lib/net/ber/core_ext/true_class.rb +9 -0
- data/lib/net/ber/core_ext.rb +72 -0
- data/lib/net/ber.rb +339 -0
- data/lib/net/ldap/dataset.rb +174 -0
- data/lib/net/ldap/entry.rb +208 -0
- data/lib/net/ldap/filter.rb +720 -0
- data/lib/net/ldap/password.rb +52 -0
- data/lib/net/ldap/pdu.rb +278 -0
- data/lib/net/ldap.rb +1536 -0
- data/lib/net/ldif.rb +34 -0
- data/lib/net/snmp.rb +295 -0
- data/lib/net-ldap.rb +1 -0
- data/spec/integration/ssl_ber_spec.rb +33 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/ber/ber_spec.rb +93 -0
- data/spec/unit/ber/core_ext/string_spec.rb +51 -0
- data/spec/unit/ldap/entry_spec.rb +51 -0
- data/spec/unit/ldap/filter_spec.rb +48 -0
- data/spec/unit/ldap_spec.rb +48 -0
- data/test/common.rb +3 -0
- data/test/test_entry.rb +59 -0
- data/test/test_filter.rb +115 -0
- data/test/test_ldif.rb +68 -0
- data/test/test_password.rb +17 -0
- data/test/test_snmp.rb +114 -0
- data/test/testdata.ldif +101 -0
- data/testserver/ldapserver.rb +210 -0
- data/testserver/testdata.ldif +101 -0
- metadata +218 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
#----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Gmail: garbagecat10
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation; either version 2 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
20
|
+
#
|
21
|
+
#---------------------------------------------------------------------------
|
22
|
+
|
23
|
+
require 'digest/sha1'
|
24
|
+
require 'digest/md5'
|
25
|
+
|
26
|
+
class Net::LDAP::Password
|
27
|
+
class << self
|
28
|
+
# Generate a password-hash suitable for inclusion in an LDAP attribute.
|
29
|
+
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
|
30
|
+
# password. This function will return a hashed representation.
|
31
|
+
#
|
32
|
+
#--
|
33
|
+
# STUB: This is here to fulfill the requirements of an RFC, which
|
34
|
+
# one?
|
35
|
+
#
|
36
|
+
# TODO, gotta do salted-sha and (maybe)salted-md5. Should we provide
|
37
|
+
# sha1 as a synonym for sha1? I vote no because then should you also
|
38
|
+
# provide ssha1 for symmetry?
|
39
|
+
def generate(type, str)
|
40
|
+
digest, digest_name = case type
|
41
|
+
when :md5
|
42
|
+
[Digest::MD5.new, 'MD5']
|
43
|
+
when :sha
|
44
|
+
[Digest::SHA1.new, 'SHA']
|
45
|
+
else
|
46
|
+
raise Net::LDAP::LdapError, "Unsupported password-hash type (#{type})"
|
47
|
+
end
|
48
|
+
digest << str.to_s
|
49
|
+
return "{#{digest_name}}#{[digest.digest].pack('m').chomp }"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/net/ldap/pdu.rb
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
# LDAP PDU support classes
|
2
|
+
#
|
3
|
+
#----------------------------------------------------------------------------
|
4
|
+
#
|
5
|
+
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# Gmail: garbagecat10
|
8
|
+
#
|
9
|
+
# This program is free software; you can redistribute it and/or modify
|
10
|
+
# it under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation; either version 2 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# This program is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with this program; if not, write to the Free Software
|
21
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
22
|
+
#
|
23
|
+
#---------------------------------------------------------------------------
|
24
|
+
|
25
|
+
require 'ostruct'
|
26
|
+
|
27
|
+
##
|
28
|
+
# Defines the Protocol Data Unit (PDU) for LDAP. An LDAP PDU always looks
|
29
|
+
# like a BER SEQUENCE with at least two elements: an INTEGER message ID
|
30
|
+
# number and an application-specific SEQUENCE. Some LDAPv3 packets also
|
31
|
+
# include an optional third element, a sequence of "controls" (see RFC 2251
|
32
|
+
# section 4.1.12 for more information).
|
33
|
+
#
|
34
|
+
# The application-specific tag in the sequence tells us what kind of packet
|
35
|
+
# it is, and each kind has its own format, defined in RFC-1777.
|
36
|
+
#
|
37
|
+
# Observe that many clients (such as ldapsearch) do not necessarily enforce
|
38
|
+
# the expected application tags on received protocol packets. This
|
39
|
+
# implementation does interpret the RFC strictly in this regard, and it
|
40
|
+
# remains to be seen whether there are servers out there that will not work
|
41
|
+
# well with our approach.
|
42
|
+
#
|
43
|
+
# Currently, we only support controls on SearchResult.
|
44
|
+
class Net::LDAP::PDU
|
45
|
+
class Error < RuntimeError; end
|
46
|
+
|
47
|
+
##
|
48
|
+
# This message packet is a bind request.
|
49
|
+
BindRequest = 0
|
50
|
+
BindResult = 1
|
51
|
+
UnbindRequest = 2
|
52
|
+
SearchRequest = 3
|
53
|
+
SearchReturnedData = 4
|
54
|
+
SearchResult = 5
|
55
|
+
ModifyResponse = 7
|
56
|
+
AddResponse = 9
|
57
|
+
DeleteResponse = 11
|
58
|
+
ModifyRDNResponse = 13
|
59
|
+
SearchResultReferral = 19
|
60
|
+
ExtendedRequest = 23
|
61
|
+
ExtendedResponse = 24
|
62
|
+
|
63
|
+
##
|
64
|
+
# The LDAP packet message ID.
|
65
|
+
attr_reader :message_id
|
66
|
+
alias_method :msg_id, :message_id
|
67
|
+
|
68
|
+
##
|
69
|
+
# The application protocol format tag.
|
70
|
+
attr_reader :app_tag
|
71
|
+
|
72
|
+
attr_reader :search_entry
|
73
|
+
attr_reader :search_referrals
|
74
|
+
attr_reader :search_parameters
|
75
|
+
attr_reader :bind_parameters
|
76
|
+
|
77
|
+
##
|
78
|
+
# Returns RFC-2251 Controls if any.
|
79
|
+
attr_reader :ldap_controls
|
80
|
+
alias_method :result_controls, :ldap_controls
|
81
|
+
# Messy. Does this functionality belong somewhere else?
|
82
|
+
|
83
|
+
def initialize(ber_object)
|
84
|
+
begin
|
85
|
+
@message_id = ber_object[0].to_i
|
86
|
+
# Grab the bottom five bits of the identifier so we know which type of
|
87
|
+
# PDU this is.
|
88
|
+
#
|
89
|
+
# This is safe enough in LDAP-land, but it is recommended that other
|
90
|
+
# approaches be taken for other protocols in the case that there's an
|
91
|
+
# app-specific tag that has both primitive and constructed forms.
|
92
|
+
@app_tag = ber_object[1].ber_identifier & 0x1f
|
93
|
+
@ldap_controls = []
|
94
|
+
rescue Exception => ex
|
95
|
+
raise Net::LDAP::PDU::Error, "LDAP PDU Format Error: #{ex.message}"
|
96
|
+
end
|
97
|
+
|
98
|
+
case @app_tag
|
99
|
+
when BindResult
|
100
|
+
parse_bind_response(ber_object[1])
|
101
|
+
when SearchReturnedData
|
102
|
+
parse_search_return(ber_object[1])
|
103
|
+
when SearchResultReferral
|
104
|
+
parse_search_referral(ber_object[1])
|
105
|
+
when SearchResult
|
106
|
+
parse_ldap_result(ber_object[1])
|
107
|
+
when ModifyResponse
|
108
|
+
parse_ldap_result(ber_object[1])
|
109
|
+
when AddResponse
|
110
|
+
parse_ldap_result(ber_object[1])
|
111
|
+
when DeleteResponse
|
112
|
+
parse_ldap_result(ber_object[1])
|
113
|
+
when ModifyRDNResponse
|
114
|
+
parse_ldap_result(ber_object[1])
|
115
|
+
when SearchRequest
|
116
|
+
parse_ldap_search_request(ber_object[1])
|
117
|
+
when BindRequest
|
118
|
+
parse_bind_request(ber_object[1])
|
119
|
+
when UnbindRequest
|
120
|
+
parse_unbind_request(ber_object[1])
|
121
|
+
when ExtendedResponse
|
122
|
+
parse_ldap_result(ber_object[1])
|
123
|
+
else
|
124
|
+
raise LdapPduError.new("unknown pdu-type: #{@app_tag}")
|
125
|
+
end
|
126
|
+
|
127
|
+
parse_controls(ber_object[2]) if ber_object[2]
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Returns a hash which (usually) defines the members :resultCode,
|
132
|
+
# :errorMessage, and :matchedDN. These values come directly from an LDAP
|
133
|
+
# response packet returned by the remote peer. Also see #result_code.
|
134
|
+
def result
|
135
|
+
@ldap_result || {}
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# This returns an LDAP result code taken from the PDU, but it will be nil
|
140
|
+
# if there wasn't a result code. That can easily happen depending on the
|
141
|
+
# type of packet.
|
142
|
+
def result_code(code = :resultCode)
|
143
|
+
@ldap_result and @ldap_result[code]
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Return serverSaslCreds, which are only present in BindResponse packets.
|
148
|
+
#--
|
149
|
+
# Messy. Does this functionality belong somewhere else? We ought to
|
150
|
+
# refactor the accessors of this class before they get any kludgier.
|
151
|
+
def result_server_sasl_creds
|
152
|
+
@ldap_result && @ldap_result[:serverSaslCreds]
|
153
|
+
end
|
154
|
+
|
155
|
+
def parse_ldap_result(sequence)
|
156
|
+
sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
|
157
|
+
@ldap_result = {
|
158
|
+
:resultCode => sequence[0],
|
159
|
+
:matchedDN => sequence[1],
|
160
|
+
:errorMessage => sequence[2]
|
161
|
+
}
|
162
|
+
end
|
163
|
+
private :parse_ldap_result
|
164
|
+
|
165
|
+
##
|
166
|
+
# A Bind Response may have an additional field, ID [7], serverSaslCreds,
|
167
|
+
# per RFC 2251 pgh 4.2.3.
|
168
|
+
def parse_bind_response(sequence)
|
169
|
+
sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP Bind Response length."
|
170
|
+
parse_ldap_result(sequence)
|
171
|
+
@ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
|
172
|
+
@ldap_result
|
173
|
+
end
|
174
|
+
private :parse_bind_response
|
175
|
+
|
176
|
+
# Definition from RFC 1777 (we're handling application-4 here).
|
177
|
+
#
|
178
|
+
# Search Response ::=
|
179
|
+
# CHOICE {
|
180
|
+
# entry [APPLICATION 4] SEQUENCE {
|
181
|
+
# objectName LDAPDN,
|
182
|
+
# attributes SEQUENCE OF SEQUENCE {
|
183
|
+
# AttributeType,
|
184
|
+
# SET OF AttributeValue
|
185
|
+
# }
|
186
|
+
# },
|
187
|
+
# resultCode [APPLICATION 5] LDAPResult
|
188
|
+
# }
|
189
|
+
#
|
190
|
+
# We concoct a search response that is a hash of the returned attribute
|
191
|
+
# values.
|
192
|
+
#
|
193
|
+
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
194
|
+
#
|
195
|
+
# This is to make them more predictable for user programs, but it may not
|
196
|
+
# be a good idea. Maybe this should be configurable.
|
197
|
+
def parse_search_return(sequence)
|
198
|
+
sequence.length >= 2 or raise Net::LDAP::PDU::Error, "Invalid Search Response length."
|
199
|
+
@search_entry = LDAP::Entry.new(sequence[0])
|
200
|
+
sequence[1].each { |seq| @search_entry[seq[0]] = seq[1] }
|
201
|
+
end
|
202
|
+
private :parse_search_return
|
203
|
+
|
204
|
+
##
|
205
|
+
# A search referral is a sequence of one or more LDAP URIs. Any number of
|
206
|
+
# search-referral replies can be returned by the server, interspersed with
|
207
|
+
# normal replies in any order.
|
208
|
+
#--
|
209
|
+
# Until I can think of a better way to do this, we'll return the referrals
|
210
|
+
# as an array. It'll be up to higher-level handlers to expose something
|
211
|
+
# reasonable to the client.
|
212
|
+
def parse_search_referral(uris)
|
213
|
+
@search_referrals = uris
|
214
|
+
end
|
215
|
+
private :parse_search_referral
|
216
|
+
|
217
|
+
##
|
218
|
+
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
|
219
|
+
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
|
220
|
+
# Octet String. If only two fields are given, the second one may be either
|
221
|
+
# criticality or data, since criticality has a default value. Someday we
|
222
|
+
# may want to come back here and add support for some of more-widely used
|
223
|
+
# controls. RFC-2696 is a good example.
|
224
|
+
def parse_controls(sequence)
|
225
|
+
@ldap_controls = sequence.map do |control|
|
226
|
+
o = OpenStruct.new
|
227
|
+
o.oid, o.criticality, o.value = control[0], control[1], control[2]
|
228
|
+
if o.criticality and o.criticality.is_a?(String)
|
229
|
+
o.value = o.criticality
|
230
|
+
o.criticality = false
|
231
|
+
end
|
232
|
+
o
|
233
|
+
end
|
234
|
+
end
|
235
|
+
private :parse_controls
|
236
|
+
|
237
|
+
# (provisional, must document)
|
238
|
+
def parse_ldap_search_request(sequence)
|
239
|
+
s = OpenStruct.new
|
240
|
+
s.base_object, s.scope, s.deref_aliases, s.size_limit, s.time_limit,
|
241
|
+
s.types_only, s.filter, s.attributes = sequence
|
242
|
+
@search_parameters = s
|
243
|
+
end
|
244
|
+
private :parse_ldap_search_request
|
245
|
+
|
246
|
+
# (provisional, must document)
|
247
|
+
def parse_bind_request sequence
|
248
|
+
s = OpenStruct.new
|
249
|
+
s.version, s.name, s.authentication = sequence
|
250
|
+
@bind_parameters = s
|
251
|
+
end
|
252
|
+
private :parse_bind_request
|
253
|
+
|
254
|
+
# (provisional, must document)
|
255
|
+
# UnbindRequest has no content so this is a no-op.
|
256
|
+
def parse_unbind_request(sequence)
|
257
|
+
nil
|
258
|
+
end
|
259
|
+
private :parse_unbind_request
|
260
|
+
end
|
261
|
+
|
262
|
+
module Net
|
263
|
+
##
|
264
|
+
# Handle the renamed constants.
|
265
|
+
def self.const_missing(name) #:nodoc:
|
266
|
+
case name.to_s
|
267
|
+
when "LdapPdu"
|
268
|
+
warn "Net::#{name} has been deprecated. Use Net::LDAP::PDU instead."
|
269
|
+
Net::LDAP::PDU
|
270
|
+
when "LdapPduError"
|
271
|
+
warn "Net::#{name} has been deprecated. Use Net::LDAP::PDU::Error instead."
|
272
|
+
Net::LDAP::PDU::Error
|
273
|
+
else
|
274
|
+
super
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end # module Net
|
278
|
+
|