pruby-net-ldap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,387 @@
1
+ # $Id: filter.rb 151 2006-08-15 08:34:53Z blackhedd $
2
+ #
3
+ #
4
+ #----------------------------------------------------------------------------
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # Gmail: garbagecat10
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this program; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+ #
24
+ #---------------------------------------------------------------------------
25
+ #
26
+ #
27
+
28
+
29
+ module Net
30
+ class LDAP
31
+
32
+
33
+ # Class Net::LDAP::Filter is used to constrain
34
+ # LDAP searches. An object of this class is
35
+ # passed to Net::LDAP#search in the parameter :filter.
36
+ #
37
+ # Net::LDAP::Filter supports the complete set of search filters
38
+ # available in LDAP, including conjunction, disjunction and negation
39
+ # (AND, OR, and NOT). This class supplants the (infamous) RFC-2254
40
+ # standard notation for specifying LDAP search filters.
41
+ #
42
+ # Here's how to code the familiar "objectclass is present" filter:
43
+ # f = Net::LDAP::Filter.pres( "objectclass" )
44
+ # The object returned by this code can be passed directly to
45
+ # the <tt>:filter</tt> parameter of Net::LDAP#search.
46
+ #
47
+ # See the individual class and instance methods below for more examples.
48
+ #
49
+ class Filter
50
+
51
+ def initialize op, a, b
52
+ @op = op
53
+ @left = a
54
+ @right = b
55
+ end
56
+
57
+ # #eq creates a filter object indicating that the value of
58
+ # a paticular attribute must be either <i>present</i> or must
59
+ # match a particular string.
60
+ #
61
+ # To specify that an attribute is "present" means that only
62
+ # directory entries which contain a value for the particular
63
+ # attribute will be selected by the filter. This is useful
64
+ # in case of optional attributes such as <tt>mail.</tt>
65
+ # Presence is indicated by giving the value "*" in the second
66
+ # parameter to #eq. This example selects only entries that have
67
+ # one or more values for <tt>sAMAccountName:</tt>
68
+ # f = Net::LDAP::Filter.eq( "sAMAccountName", "*" )
69
+ #
70
+ # To match a particular range of values, pass a string as the
71
+ # second parameter to #eq. The string may contain one or more
72
+ # "*" characters as wildcards: these match zero or more occurrences
73
+ # of any character. Full regular-expressions are <i>not</i> supported
74
+ # due to limitations in the underlying LDAP protocol.
75
+ # This example selects any entry with a <tt>mail</tt> value containing
76
+ # the substring "anderson":
77
+ # f = Net::LDAP::Filter.eq( "mail", "*anderson*" )
78
+ #--
79
+ # Removed gt and lt. They ain't in the standard!
80
+ #
81
+ def Filter::eq attribute, value; Filter.new :eq, attribute, value; end
82
+ def Filter::ne attribute, value; Filter.new :ne, attribute, value; end
83
+ #def Filter::gt attribute, value; Filter.new :gt, attribute, value; end
84
+ #def Filter::lt attribute, value; Filter.new :lt, attribute, value; end
85
+ def Filter::ge attribute, value; Filter.new :ge, attribute, value; end
86
+ def Filter::le attribute, value; Filter.new :le, attribute, value; end
87
+
88
+ # #pres( attribute ) is a synonym for #eq( attribute, "*" )
89
+ #
90
+ def Filter::pres attribute; Filter.eq attribute, "*"; end
91
+
92
+ # operator & ("AND") is used to conjoin two or more filters.
93
+ # This expression will select only entries that have an <tt>objectclass</tt>
94
+ # attribute AND have a <tt>mail</tt> attribute that begins with "George":
95
+ # f = Net::LDAP::Filter.pres( "objectclass" ) & Net::LDAP::Filter.eq( "mail", "George*" )
96
+ #
97
+ def & filter; Filter.new :and, self, filter; end
98
+
99
+ # operator | ("OR") is used to disjoin two or more filters.
100
+ # This expression will select entries that have either an <tt>objectclass</tt>
101
+ # attribute OR a <tt>mail</tt> attribute that begins with "George":
102
+ # f = Net::LDAP::Filter.pres( "objectclass" ) | Net::LDAP::Filter.eq( "mail", "George*" )
103
+ #
104
+ def | filter; Filter.new :or, self, filter; end
105
+
106
+
107
+ #
108
+ # operator ~ ("NOT") is used to negate a filter.
109
+ # This expression will select only entries that <i>do not</i> have an <tt>objectclass</tt>
110
+ # attribute:
111
+ # f = ~ Net::LDAP::Filter.pres( "objectclass" )
112
+ #
113
+ #--
114
+ # This operator can't be !, evidently. Try it.
115
+ # Removed GT and LT. They're not in the RFC.
116
+ def ~@; Filter.new :not, self, nil; end
117
+
118
+
119
+ def to_s
120
+ case @op
121
+ when :ne
122
+ "(!(#{@left}=#{@right}))"
123
+ when :eq
124
+ "(#{@left}=#{@right})"
125
+ #when :gt
126
+ # "#{@left}>#{@right}"
127
+ #when :lt
128
+ # "#{@left}<#{@right}"
129
+ when :ge
130
+ "#{@left}>=#{@right}"
131
+ when :le
132
+ "#{@left}<=#{@right}"
133
+ when :and
134
+ "(&(#{@left})(#{@right}))"
135
+ when :or
136
+ "(|(#{@left})(#{@right}))"
137
+ when :not
138
+ "(!(#{@left}))"
139
+ else
140
+ raise "invalid or unsupported operator in LDAP Filter"
141
+ end
142
+ end
143
+
144
+
145
+ #--
146
+ # to_ber
147
+ # Filter ::=
148
+ # CHOICE {
149
+ # and [0] SET OF Filter,
150
+ # or [1] SET OF Filter,
151
+ # not [2] Filter,
152
+ # equalityMatch [3] AttributeValueAssertion,
153
+ # substrings [4] SubstringFilter,
154
+ # greaterOrEqual [5] AttributeValueAssertion,
155
+ # lessOrEqual [6] AttributeValueAssertion,
156
+ # present [7] AttributeType,
157
+ # approxMatch [8] AttributeValueAssertion
158
+ # }
159
+ #
160
+ # SubstringFilter
161
+ # SEQUENCE {
162
+ # type AttributeType,
163
+ # SEQUENCE OF CHOICE {
164
+ # initial [0] LDAPString,
165
+ # any [1] LDAPString,
166
+ # final [2] LDAPString
167
+ # }
168
+ # }
169
+ #
170
+ # Parsing substrings is a little tricky.
171
+ # We use the split method to break a string into substrings
172
+ # delimited by the * (star) character. But we also need
173
+ # to know whether there is a star at the head and tail
174
+ # of the string. A Ruby particularity comes into play here:
175
+ # if you split on * and the first character of the string is
176
+ # a star, then split will return an array whose first element
177
+ # is an _empty_ string. But if the _last_ character of the
178
+ # string is star, then split will return an array that does
179
+ # _not_ add an empty string at the end. So we have to deal
180
+ # with all that specifically.
181
+ #
182
+ def to_ber
183
+ case @op
184
+ when :eq
185
+ if @right == "*" # present
186
+ @left.to_s.to_ber_contextspecific 7
187
+ elsif @right =~ /[\*]/ #substring
188
+ ary = @right.split( /[\*]+/ )
189
+ final_star = @right =~ /[\*]$/
190
+ initial_star = ary.first == "" and ary.shift
191
+
192
+ seq = []
193
+ unless initial_star
194
+ seq << ary.shift.to_ber_contextspecific(0)
195
+ end
196
+ n_any_strings = ary.length - (final_star ? 0 : 1)
197
+ #p n_any_strings
198
+ n_any_strings.times {
199
+ seq << ary.shift.to_ber_contextspecific(1)
200
+ }
201
+ unless final_star
202
+ seq << ary.shift.to_ber_contextspecific(2)
203
+ end
204
+ [@left.to_s.to_ber, seq.to_ber].to_ber_contextspecific 4
205
+ else #equality
206
+ [@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 3
207
+ end
208
+ when :ge
209
+ [@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 5
210
+ when :le
211
+ [@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 6
212
+ when :and
213
+ ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten
214
+ ary.map {|a| a.to_ber}.to_ber_contextspecific( 0 )
215
+ when :or
216
+ ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten
217
+ ary.map {|a| a.to_ber}.to_ber_contextspecific( 1 )
218
+ when :not
219
+ [@left.to_ber].to_ber_contextspecific 2
220
+ else
221
+ # ERROR, we'll return objectclass=* to keep things from blowing up,
222
+ # but that ain't a good answer and we need to kick out an error of some kind.
223
+ raise "unimplemented search filter"
224
+ end
225
+ end
226
+
227
+ #--
228
+ # coalesce
229
+ # This is a private helper method for dealing with chains of ANDs and ORs
230
+ # that are longer than two. If BOTH of our branches are of the specified
231
+ # type of joining operator, then return both of them as an array (calling
232
+ # coalesce recursively). If they're not, then return an array consisting
233
+ # only of self.
234
+ #
235
+ def coalesce operator
236
+ if @op == operator
237
+ [@left.coalesce( operator ), @right.coalesce( operator )]
238
+ else
239
+ [self]
240
+ end
241
+ end
242
+
243
+
244
+
245
+ #--
246
+ # We get a Ruby object which comes from parsing an RFC-1777 "Filter"
247
+ # object. Convert it to a Net::LDAP::Filter.
248
+ # TODO, we're hardcoding the RFC-1777 BER-encodings of the various
249
+ # filter types. Could pull them out into a constant.
250
+ #
251
+ def Filter::parse_ldap_filter obj
252
+ case obj.ber_identifier
253
+ when 0x87 # present. context-specific primitive 7.
254
+ Filter.eq( obj.to_s, "*" )
255
+ when 0xa3 # equalityMatch. context-specific constructed 3.
256
+ Filter.eq( obj[0], obj[1] )
257
+ else
258
+ raise LdapError.new( "unknown ldap search-filter type: #{obj.ber_identifier}" )
259
+ end
260
+ end
261
+
262
+
263
+ #--
264
+ # We got a hash of attribute values.
265
+ # Do we match the attributes?
266
+ # Return T/F, and call match recursively as necessary.
267
+ def match entry
268
+ case @op
269
+ when :eq
270
+ if @right == "*"
271
+ l = entry[@left] and l.length > 0
272
+ else
273
+ l = entry[@left] and l = l.to_a and l.index(@right)
274
+ end
275
+ else
276
+ raise LdapError.new( "unknown filter type in match: #{@op}" )
277
+ end
278
+ end
279
+
280
+ # Converts an LDAP filter-string (in the prefix syntax specified in RFC-2254)
281
+ # to a Net::LDAP::Filter.
282
+ def self.construct ldap_filter_string
283
+ FilterParser.new(ldap_filter_string).filter
284
+ end
285
+
286
+ # Synonym for #construct.
287
+ # to a Net::LDAP::Filter.
288
+ def self.from_rfc2254 ldap_filter_string
289
+ construct ldap_filter_string
290
+ end
291
+
292
+ end # class Net::LDAP::Filter
293
+
294
+
295
+
296
+ class FilterParser #:nodoc:
297
+
298
+ attr_reader :filter
299
+
300
+ def initialize str
301
+ require 'strscan'
302
+ @filter = parse( StringScanner.new( str )) or raise Net::LDAP::LdapError.new( "invalid filter syntax" )
303
+ end
304
+
305
+ def parse scanner
306
+ parse_filter_branch(scanner) or parse_paren_expression(scanner)
307
+ end
308
+
309
+ def parse_paren_expression scanner
310
+ if scanner.scan /\s*\(\s*/
311
+ b = if scanner.scan /\s*\&\s*/
312
+ a = nil
313
+ branches = []
314
+ while br = parse_paren_expression(scanner)
315
+ branches << br
316
+ end
317
+ if branches.length >= 2
318
+ a = branches.shift
319
+ while branches.length > 0
320
+ a = a & branches.shift
321
+ end
322
+ a
323
+ end
324
+ elsif scanner.scan /\s*\|\s*/
325
+ # TODO: DRY!
326
+ a = nil
327
+ branches = []
328
+ while br = parse_paren_expression(scanner)
329
+ branches << br
330
+ end
331
+ if branches.length >= 2
332
+ a = branches.shift
333
+ while branches.length > 0
334
+ a = a | branches.shift
335
+ end
336
+ a
337
+ end
338
+ elsif scanner.scan /\s*\!\s*/
339
+ br = parse_paren_expression(scanner)
340
+ if br
341
+ ~ br
342
+ end
343
+ else
344
+ parse_filter_branch( scanner )
345
+ end
346
+
347
+ if b and scanner.scan( /\s*\)\s*/ )
348
+ b
349
+ end
350
+ end
351
+ end
352
+
353
+ # Added a greatly-augmented filter contributed by Andre Nathan
354
+ # for detecting special characters in values. (15Aug06)
355
+ def parse_filter_branch scanner
356
+ scanner.scan /\s*/
357
+ if token = scanner.scan( /[\w\-_]+/ )
358
+ scanner.scan /\s*/
359
+ if op = scanner.scan( /\=|\<\=|\<|\>\=|\>|\!\=/ )
360
+ scanner.scan /\s*/
361
+ #if value = scanner.scan( /[\w\*\.]+/ ) (ORG)
362
+ if value = scanner.scan( /[\w\*\.\+\-@=#\$%&!]+/ )
363
+ case op
364
+ when "="
365
+ Filter.eq( token, value )
366
+ when "!="
367
+ Filter.ne( token, value )
368
+ when "<"
369
+ Filter.lt( token, value )
370
+ when "<="
371
+ Filter.le( token, value )
372
+ when ">"
373
+ Filter.gt( token, value )
374
+ when ">="
375
+ Filter.ge( token, value )
376
+ end
377
+ end
378
+ end
379
+ end
380
+ end
381
+
382
+ end # class Net::LDAP::FilterParser
383
+
384
+ end # class Net::LDAP
385
+ end # module Net
386
+
387
+
@@ -0,0 +1,205 @@
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
+
@@ -0,0 +1,64 @@
1
+ # $Id: psw.rb 73 2006-04-24 21:59:35Z blackhedd $
2
+ #
3
+ #
4
+ #----------------------------------------------------------------------------
5
+ #
6
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
7
+ #
8
+ # Gmail: garbagecat10
9
+ #
10
+ # This program is free software; you can redistribute it and/or modify
11
+ # it under the terms of the GNU General Public License as published by
12
+ # the Free Software Foundation; either version 2 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # This program is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU General Public License
21
+ # along with this program; if not, write to the Free Software
22
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
+ #
24
+ #---------------------------------------------------------------------------
25
+ #
26
+ #
27
+
28
+
29
+ module Net
30
+ class LDAP
31
+
32
+
33
+ class Password
34
+ class << self
35
+
36
+ # Generate a password-hash suitable for inclusion in an LDAP attribute.
37
+ # Pass a hash type (currently supported: :md5 and :sha) and a plaintext
38
+ # password. This function will return a hashed representation.
39
+ # STUB: This is here to fulfill the requirements of an RFC, which one?
40
+ # TODO, gotta do salted-sha and (maybe) salted-md5.
41
+ # Should we provide sha1 as a synonym for sha1? I vote no because then
42
+ # should you also provide ssha1 for symmetry?
43
+ def generate( type, str )
44
+ case type
45
+ when :md5
46
+ require 'md5'
47
+ "{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
48
+ when :sha
49
+ require 'sha1'
50
+ "{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
51
+ # when ssha
52
+ else
53
+ raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" )
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+
61
+ end # class LDAP
62
+ end # module Net
63
+
64
+