ruby-net-ldap/lib/net/ldap/entry.rb

267 lines
8.1 KiB
Ruby
Raw Normal View History

2006-04-25 16:03:11 +02:00
# LDAP Entry (search-result) 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 LDAP
2010-03-18 09:28:27 +01:00
# Objects of this class represent individual entries in an LDAP directory.
# User code generally does not instantiate this class. Net::LDAP#search
# provides objects of this class to user code, either as block parameters or
# as return values.
2006-05-02 02:10:24 +02:00
#
2010-03-18 09:28:27 +01:00
# In LDAP-land, an "entry" is a collection of attributes that are uniquely
# and globally identified by a DN ("Distinguished Name"). Attributes are
# identified by short, descriptive words or phrases. Although a directory is
2006-05-02 02:10:24 +02:00
# free to implement any attribute name, most of them follow rigorous
2010-03-18 09:28:27 +01:00
# standards so that the range of commonly-encountered attribute names is not
# large.
2006-05-02 02:10:24 +02:00
#
2010-03-18 09:28:27 +01:00
# An attribute name is case-insensitive. Most directories also restrict the
# range of characters allowed in attribute names. To simplify handling
# attribute names, Net::LDAP::Entry internally converts them to a standard
# format. Therefore, the methods which take attribute names can take Strings
# or Symbols, and work correctly regardless of case or capitalization.
2006-05-02 02:10:24 +02:00
#
2010-03-18 09:28:27 +01:00
# An attribute consists of zero or more data items called <i>values.</i> An
# entry is the combination of a unique DN, a set of attribute names, and a
# (possibly-empty) array of values for each attribute.
2006-05-02 02:10:24 +02:00
#
2010-03-18 09:28:27 +01:00
# Class Net::LDAP::Entry provides convenience methods for dealing with LDAP
# entries. In addition to the methods documented below, you may access
# individual attributes of an entry simply by giving the attribute name as
2006-05-04 14:30:29 +02:00
# the name of a method call. For example:
2010-03-18 09:28:27 +01:00
#
# ldap.search( ... ) do |entry|
# puts "Common name: #{entry.cn}"
# puts "Email addresses:"
# entry.mail.each {|ma| puts ma}
# end
#
# If you use this technique to access an attribute that is not present in a
# particular Entry object, a NoMethodError exception will be raised.
2006-05-02 02:10:24 +02:00
#
#--
2010-03-18 09:28:27 +01:00
# Ugly problem to fix someday: We key off the internal hash with a canonical
# form of the attribute name: convert to a string, downcase, then take the
# symbol. Unfortunately we do this in at least three places. Should do it in
# ONE place.
#
2006-04-25 16:03:11 +02:00
class Entry
2006-05-02 02:10:24 +02:00
# This constructor is not generally called by user code.
#
2006-05-02 02:10:24 +02:00
def initialize dn = nil # :nodoc:
@myhash = {}
2006-04-25 22:15:46 +02:00
@myhash[:dn] = [dn]
2006-04-25 16:03:11 +02:00
end
def _dump depth
to_ldif
end
class << self
def _load entry
from_single_ldif_string entry
end
end
2006-04-25 16:03:11 +02:00
#--
# Discovered bug, 26Aug06: I noticed that we're not converting the
# incoming value to an array if it isn't already one.
def []=(name, value) # :nodoc:
sym = attribute_name(name)
value = [value] unless value.is_a?(Array)
2006-04-25 16:03:11 +02:00
@myhash[sym] = value
end
#--
# We have to deal with this one as we do with []= because this one and not
# the other one gets called in formulations like entry["CN"] << cn.
#
def [](name) # :nodoc:
name = attribute_name(name) unless name.is_a?(Symbol)
@myhash[name] || []
2006-04-25 16:03:11 +02:00
end
2006-05-02 02:10:24 +02:00
# Returns the dn of the Entry as a String.
2006-04-25 16:03:11 +02:00
def dn
self[:dn][0].to_s
2006-04-25 16:03:11 +02:00
end
2006-05-02 02:10:24 +02:00
# Returns an array of the attribute names present in the Entry.
2006-04-25 16:03:11 +02:00
def attribute_names
@myhash.keys
end
2006-05-02 02:10:24 +02:00
# Accesses each of the attributes present in the Entry.
# Calls a user-supplied block with each attribute in turn,
# passing two arguments to the block: a Symbol giving
# the name of the attribute, and a (possibly empty)
# Array of data values.
#
2006-04-25 22:15:46 +02:00
def each
if block_given?
2006-05-02 02:10:24 +02:00
attribute_names.each {|a|
attr_name,values = a,self[a]
yield attr_name, values
}
2006-04-25 22:15:46 +02:00
end
end
alias_method :each_attribute, :each
2006-04-25 16:03:11 +02:00
2006-08-18 21:41:03 +02:00
# Converts the Entry to a String, representing the
# Entry's attributes in LDIF format.
#--
2006-08-18 21:41:03 +02:00
def to_ldif
ary = []
ary << "dn: #{dn}\n"
v2 = "" # temp value, save on GC
2006-08-18 21:41:03 +02:00
each_attribute do |k,v|
unless k == :dn
v.each {|v1|
v2 = if (k == :userpassword) || is_attribute_value_binary?(v1)
2006-08-31 15:23:25 +02:00
": #{Base64.encode64(v1).chomp.gsub(/\n/m,"\n ")}"
else
" #{v1}"
end
ary << "#{k}:#{v2}\n"
}
end
2006-08-18 21:41:03 +02:00
end
ary << "\n"
ary.join
end
#--
# TODO, doesn't support broken lines.
# It generates a SINGLE Entry object from an incoming LDIF stream which is
# of course useless for big LDIF streams that encode many objects.
#
# DO NOT DOCUMENT THIS METHOD UNTIL THESE RESTRICTIONS ARE LIFTED.
#
# As it is, it's useful for unmarshalling objects that we create, but not
# for reading arbitrary LDIF files. Eventually, we should have a class
# method that parses large LDIF streams into individual LDIF blocks
# (delimited by blank lines) and passes them here.
#
class << self
def from_single_ldif_string ldif
entry = Entry.new
entry[:dn] = []
ldif.split(/\r?\n/m).each {|line|
break if line.length == 0
if line =~ /\A([\w]+):(:?)[\s]*/
entry[$1] <<= if $2 == ':'
Base64.decode64($')
else
$'
end
end
}
entry.dn ? entry : nil
end
end
#--
# Part of the support for getter and setter style access to attributes.
#
def respond_to?(sym)
name = attribute_name(sym)
return true if valid_attribute?(name)
return super
end
2006-08-18 21:41:03 +02:00
2006-05-04 14:24:57 +02:00
#--
# Supports getter and setter style access for all the attributes that this
# entry holds.
2006-05-04 14:24:57 +02:00
#
def method_missing sym, *args, &block # :nodoc:
name = attribute_name(sym)
if valid_attribute? name
if setter?(sym) && args.size == 1
value = args.first
value = [value] unless value.instance_of?(Array)
self[name]= value
return value
elsif args.empty?
return self[name]
end
end
super
end
def write
end
private
#--
# Internal convenience method. It seems like the standard
# approach in most LDAP tools to base64 encode an attribute
# value if its first or last byte is nonprintable, or if
# it's a password. But that turns out to be not nearly good
# enough. There are plenty of A/D attributes that are binary
# in the middle. This is probably a nasty performance killer.
def is_attribute_value_binary? value
v = value.to_s
v.each_byte {|byt|
return true if (byt < 32) || (byt > 126)
}
if v[0..0] == ':' or v[0..0] == '<'
return true
end
false
end
# Returns the symbol that can be used to access the attribute that
# sym_or_str designates.
#
def attribute_name(sym_or_str)
str = sym_or_str.to_s.downcase
# Does str match 'something='? Still only returns :something
return str[0...-1].to_sym if str.size>1 && str[-1] == ?=
return str.to_sym
end
# Given a valid attribute symbol, returns true.
#
def valid_attribute?(attr_name)
attribute_names.include?(attr_name)
end
def setter?(sym)
sym.to_s[-1] == ?=
end
2006-04-25 16:03:11 +02:00
end # class Entry
end # class LDAP
end # module Net