require 'ldap' require 'ldap/schema' module LDAP class Schema2 < Schema @@cache = {} # attr # # This is just like LDAP::Schema#attr except that it allows # look up in any of the given keys. # e.g. # attr('attributeTypes', 'cn', 'DESC') # attr('ldapSyntaxes', '1.3.6.1.4.1.1466.115.121.1.5', 'DESC') def attr(sub, type, at) return '' if sub.empty? return '' if type.empty? return '' if at.empty? # Check already parsed options first if @@cache.has_key? sub \ and @@cache[sub].has_key? type \ and @@cache[sub][type].has_key? at return @@cache[sub][type][at].dup end # Initialize anything that is required unless @@cache.has_key? sub @@cache[sub] = {} end unless @@cache[sub].has_key? type @@cache[sub][type] = {} end at = at.upcase self[sub].each do |s| line = '' if type[0..0] =~ /[0-9]/ line = s if s =~ /\(\s+#{type}\s+(?:[A-Z]|\))/ else line = s if s =~ /NAME\s+\(?.*'#{type}'.*\)?\s+(?:[A-Z]|\))/ end # I need to check, but I think some of these matchs # overlap. I'll need to check these when I'm less sleepy. multi = '' case line when /#{at}\s+[\)A-Z]/ @@cache[sub][type][at] = ['TRUE'] return ['TRUE'] when /#{at}\s+'(.+?)'/ @@cache[sub][type][at] = [$1] return [$1] when /#{at}\s+\((.+?)\)/ multi = $1 when /#{at}\s+\(([\w\d\s\.]+)\)/ multi = $1 when /#{at}\s+([\w\d\.]+)/ @@cache[sub][type][at] = [$1] return [$1] end # Split up multiple matches # if oc then it is sep'd by $ # if attr then bu spaces if multi.match(/\$/) @@cache[sub][type][at] = multi.split("$").collect{|attr| attr.strip} return @@cache[sub][type][at].dup elsif not multi.empty? @@cache[sub][type][at] = multi.gsub(/'/, '').split(' ').collect{|attr| attr.strip} return @@cache[sub][type][at].dup end end @@cache[sub][type][at] = [] return [] end # attribute_aliases # # Returns all names from the LDAP schema for the # attribute given. def attribute_aliases(attr) attr('attributeTypes', attr, 'NAME') end # attribute aliases # read_only? # # Returns true if an attribute is read-only # NO-USER-MODIFICATION def read_only?(attr) result = attr('attributeTypes', attr, 'NO-USER-MODIFICATION') return true if result[0] == 'TRUE' return false end # single_value? # # Returns true if an attribute can only have one # value defined # SINGLE-VALUE def single_value?(attr) result = attr('attributeTypes', attr, 'SINGLE-VALUE') return true if result[0] == 'TRUE' return false end # binary? # # Returns true if the given attribute's syntax # is X-NOT-HUMAN-READABLE or X-BINARY-TRANSFER-REQUIRED def binary?(attr) # Get syntax OID syntax = attr('attributeTypes', attr, 'SYNTAX') return false if syntax.empty? # This seems to indicate binary result = attr('ldapSyntaxes', syntax[0], 'X-NOT-HUMAN-READABLE') return true if result[0] == "TRUE" # Get if binary transfer is required (non-binary types) # Usually these have the above tag result = attr('ldapSyntaxes', syntax[0], 'X-BINARY-TRANSFER-REQUIRED') return true if result[0] == "TRUE" return false end # binary? # binary_required? # # Returns true if the value MUST be transferred in binary def binary_required?(attr) # Get syntax OID syntax = attr('attributeTypes', attr, 'SYNTAX') return false if syntax.empty? # Get if binary transfer is required (non-binary types) # Usually these have the above tag result = attr('ldapSyntaxes', syntax[0], 'X-BINARY-TRANSFER-REQUIRED') return true if result[0] == "TRUE" return false end # binary_required? end # Schema2 class Conn def schema(base = nil, attrs = nil, sec = 0, usec = 0) attrs ||= [ 'objectClasses', 'attributeTypes', 'matchingRules', 'matchingRuleUse', 'dITStructureRules', 'dITContentRules', 'nameForms', 'ldapSyntaxes', ] base ||= root_dse(['subschemaSubentry'], sec, usec)[0]['subschemaSubentry'][0] base ||= 'cn=schema' ent = search2(base, LDAP_SCOPE_BASE, '(objectClass=subschema)', attrs, false, sec, usec) return Schema2.new(ent[0]) end end end # end LDAP