scashin133-net-ldap 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
+