Schema

class
Superclass
Object
Included Modules
Treequel::Constants::Patterns
Extended With
Loggability

This is an object that is used to parse and query a directory's schema

== Authors

Copyright © 2008-2016, Michael Granger and Mahlon E. Smith All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This is a collection of classes for representing objectClasses in a Treequel::Schema.

== Authors

Copyright © 2008-2016, Michael Granger and Mahlon E. Smith All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Constants

OBJECTCLASS_TYPES

The types of objectClass as specified in the schema, along with which Ruby class corresponds to it. Each class registers itself as it's defined.

Attributes

attribute_types[R]

The hash of Treequel::Schema::AttributeType objects, keyed by OID and any associated NAME attributes (as Symbols), that describe the attributeTypes in the directory's schema.

ldap_syntaxes[R]

The hash of Treequel::Schema::LDAPSyntax objects, keyed by OID, that describe the syntaxes in the directory's schema.

matching_rule_use[R]

The hash of Treequel::Schema::MatchingRuleUse objects, keyed by OID and any associated NAME attributes (as Symbols), that describe the attributes to which a matchingRule can be applied.

matching_rule_uses[R]

The hash of Treequel::Schema::MatchingRuleUse objects, keyed by OID and any associated NAME attributes (as Symbols), that describe the attributes to which a matchingRule can be applied.

matching_rules[R]

The hash of Treequel::Schema::MatchingRule objects, keyed by OID and any associated NAME attributes (as Symbols), that describe the matchingRules int he directory's schema.

object_classes[R]

The table of Treequel::Schema::ObjectClass objects, keyed by OID and any associated NAME attributes (as Symbols), that describes the objectClasses in the directory's schema.

Public Class Methods

anchor
new( hash )

Create a new Treequel::Schema from the specified hash. The hash should be of the same form as the one returned by LDAP::Conn.schema, i.e., a Hash of Arrays associated with the keys “objectClasses”, “ldapSyntaxes”, “matchingRuleUse”, “attributeTypes”, and “matchingRules”.

# File lib/treequel/schema.rb, line 177
def initialize( hash )
        @object_classes     = self.parse_objectclasses( hash['objectClasses'] || [] )
        @attribute_types    = self.parse_attribute_types( hash['attributeTypes'] || [] )
        @ldap_syntaxes      = self.parse_ldap_syntaxes( hash['ldapSyntaxes'] || [] )
        @matching_rules     = self.parse_matching_rules( hash['matchingRules'] || [] )
        @matching_rule_uses = self.parse_matching_rule_uses( hash['matchingRuleUse'] || [] )
end
anchor
oids( *oids )

Return a description of the given oids suitable for inclusion in an RFC4512-style schema description entry.

# File lib/treequel/schema.rb, line 158
def self::oids( *oids )
        oids.flatten!
        if oids.length > 1
                return "( %s )" % [ oids.join(" $ ") ]
        else
                return oids.first
        end
end
anchor
parse_names( names )

Parse the given short names string (a 'qdescrs' in the BNF) into an Array of zero or more Strings.

# File lib/treequel/schema.rb, line 97
def self::parse_names( names )
        # Treequel.logger.debug "  parsing NAME attribute from: %p" % [ names ]

        # Unspecified
        if names.nil?
                # Treequel.logger.debug "    no NAME attribute"
                return []

        # Multi-value
        elsif names =~ /#{LPAREN} #{WSP} (#{QDESCRLIST}) #{WSP} #{RPAREN}/x
                # Treequel.logger.debug "    parsing a NAME list from %p" % [ $1 ]
                return $1.scan( QDESCR ).collect {|qd| qd[1..-2].untaint.to_sym }

        # Single-value
        else
                # Return the name without the quotes
                # Treequel.logger.debug "    dequoting a single NAME"
                return [ names[1..-2].untaint.to_sym ]
        end
end
anchor
parse_oid( oidstring )

Parse a single OID into either a numeric OID string or a Symbol.

# File lib/treequel/schema.rb, line 86
def self::parse_oid( oidstring )
        if oidstring =~ NUMERICOID
                return oidstring.untaint
        else
                return oidstring.untaint.to_sym
        end
end
anchor
parse_oids( oidstring )

Parse the given oidstring into an Array of OIDs, with Strings for numeric OIDs and Symbols for aliases.

# File lib/treequel/schema.rb, line 61
def self::parse_oids( oidstring )
        return [] unless oidstring

        unless /^ #{OIDS} $/x.match( oidstring.strip )
                raise Treequel::ParseError, "couldn't find an OIDLIST in %p" % [ oidstring ]
        end

        oids = $MATCH
        # Treequel.logger.debug "  found OIDs: %p" % [ oids ]

        # If it's an OIDLIST, strip off leading and trailing parens and whitespace, then split
        # on ' $ ' and parse each OID
        if oids.include?( '$' )
                parse_oid = self.method( :parse_oid )
                return $MATCH[1..-2].strip.split( /#{WSP} #{DOLLAR} #{WSP}/x ).collect( &parse_oid )

        else
                return [ self.parse_oid(oids) ]

        end

end
anchor
qdescrs( *descriptors )

Return a description of the given descriptors suitable for inclusion in an RFC4512-style schema description entry.

# File lib/treequel/schema.rb, line 129
def self::qdescrs( *descriptors )
        descriptors.flatten!
        if descriptors.length > 1
                return "( %s )" % [ descriptors.collect {|str| self.qdstring(str) }.join(" ") ]
        else
                return self.qdstring( descriptors.first )
        end
end
anchor
qdstring( string )

Escape and quote the specified string according to the rules in RFC4512/2252.

# File lib/treequel/schema.rb, line 151
def self::qdstring( string )
        return "'%s'" % [ string.to_s.gsub(/\/, '\\5c').gsub(/'/, '\\27') ]
end
anchor
strict_parse_mode=( newval )

Set the strict-parsing flag. Setting this to a true value causes schema-parsing errors to be propagated to the caller instead of handled by the constructor, which is the default behavior.

# File lib/treequel/schema.rb, line 48
def self::strict_parse_mode=( newval )
        @strict_parse_mode = newval ? true : false
end
anchor
strict_parse_mode?()

Test whether or not strict-parsing mode is in effect.

# File lib/treequel/schema.rb, line 54
def self::strict_parse_mode?
        return @strict_parse_mode ? true : false
end
anchor
unquote_desc( desc )

Return a new string which is desc with quotes stripped and any escaped characters un-escaped.

# File lib/treequel/schema.rb, line 121
def self::unquote_desc( desc )
        return nil if desc.nil?
        return desc.gsub( QQ, "'" ).gsub( QS, '\' )[ 1..-2 ]
end

Public Instance Methods

anchor
inspect()

Return a human-readable representation of the object suitable for debugging.

# File lib/treequel/schema.rb, line 228
def inspect
        return %Q{#<%s:0x%0x %s>} % [
                self.class.name,
                self.object_id / 2,
                self.ivar_descriptions.join( ', ' ),
        ]
end
anchor
operational_attribute_types()

Return the Treequel::Schema::AttributeType objects that correspond to the operational attributes that are supported by the directory.

# File lib/treequel/schema.rb, line 214
def operational_attribute_types
        return self.attribute_types.values.find_all {|attrtype| attrtype.operational? }.uniq
end
anchor
to_s()

Return the schema as a human-readable english string.

# File lib/treequel/schema.rb, line 220
def to_s
        parts = [ "Schema:" ]
        parts << self.ivar_descriptions.collect {|desc| '  ' + desc }
        return parts.join( $/ )
end

Protected Instance Methods

anchor
ivar_descriptions()

Return descriptions of the schema's artifacts, and how many of each it has.

# File lib/treequel/schema.rb, line 353
def ivar_descriptions
        self.instance_variables.sort.collect do |ivar|
                next unless ivar.respond_to?( :length )
                len = self.instance_variable_get( ivar ).length
                "%d %s" % [ len, ivar.to_s.gsub(/_/, ' ')[1..-1] ]
        end
end
anchor
parse_attribute_types( descriptions )

Parse the given attributeType descriptions into Treequel::Schema::AttributeType objects and return them as a Hash keyed both by numeric OID and by each of its NAME attributes (if it has any).

# File lib/treequel/schema.rb, line 266
def parse_attribute_types( descriptions )
        return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
                begin
                        attrtype = Treequel::Schema::AttributeType.parse( self, desc )
                        table[ attrtype.oid ] = attrtype
                        attrtype.names.inject( table ) {|h, name| h[name] = attrtype; h }
                rescue Treequel::ParseError => err
                        if self.class.strict_parse_mode?
                                raise
                        else
                                self.log.warn( err.message )
                        end
                end

                table
        end
end
anchor
parse_ldap_syntaxes( descriptions )

Parse the given LDAP syntax descriptions into Treequel::Schema::LDAPSyntax objects and return them as a Hash keyed by numeric OID.

# File lib/treequel/schema.rb, line 287
def parse_ldap_syntaxes( descriptions )
        descriptions ||= []
        return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
                begin
                        syntax = Treequel::Schema::LDAPSyntax.parse( self, desc )
                        table[ syntax.oid ] = syntax
                rescue Treequel::ParseError => err
                        if self.class.strict_parse_mode?
                                raise
                        else
                                self.log.warn( err.message )
                        end
                end

                table
        end
end
anchor
parse_matching_rule_uses( descriptions )

Parse the given matchingRuleUse descriptions into Treequel::Schema::MatchingRuleUse objects and return them as a Hash keyed both by numeric OID and by each of its NAME attributes (if it has any).

# File lib/treequel/schema.rb, line 332
def parse_matching_rule_uses( descriptions )
        descriptions ||= []
        return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
                begin
                        ruleuse = Treequel::Schema::MatchingRuleUse.parse( self, desc )
                        table[ ruleuse.oid ] = ruleuse
                        ruleuse.names.inject( table ) {|h, name| h[name] = ruleuse; h }
                rescue Treequel::ParseError => err
                        if self.class.strict_parse_mode?
                                raise
                        else
                                self.log.warn( err.message )
                        end
                end

                table
        end
end
anchor
parse_matching_rules( descriptions )

Parse the given matchingRule descriptions into Treequel::Schema::MatchingRule objects and return them as a Hash keyed both by numeric OID and by each of its NAME attributes (if it has any).

# File lib/treequel/schema.rb, line 309
def parse_matching_rules( descriptions )
        descriptions ||= []
        return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
                begin
                        rule = Treequel::Schema::MatchingRule.parse( self, desc )
                        table[ rule.oid ] = rule
                        rule.names.inject( table ) {|h, name| h[name] = rule; h }
                rescue Treequel::ParseError => err
                        if self.class.strict_parse_mode?
                                raise
                        else
                                self.log.warn( err.message )
                        end
                end

                table
        end
end
anchor
parse_objectclasses( descriptions )

Parse the given objectClass descriptions into Treequel::Schema::ObjectClass objects, and return them as a Hash keyed both by numeric OID and by each of its NAME attributes (if it has any).

# File lib/treequel/schema.rb, line 244
def parse_objectclasses( descriptions )
        return descriptions.inject( Treequel::Schema::Table.new ) do |table, desc|
                begin
                        oc = Treequel::Schema::ObjectClass.parse( self, desc )
                        table[ oc.oid ] = oc
                        oc.names.inject( table ) {|h, name| h[name] = oc; h }
                rescue Treequel::ParseError => err
                        if self.class.strict_parse_mode?
                                raise
                        else
                                self.log.warn( err.message )
                        end
                end

                table
        end
end