You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
7.4 KiB

module LDAP
class Server
# A class which describes LDAP SyntaxDescriptions. For now there is
# a global pool of Syntax objects (rather than each Schema object
# having its own set)
class Syntax
attr_reader :oid, :nhr, :binary, :desc
# Create a new Syntax object
def initialize(oid, desc=nil, opt={}, &blk)
@oid = oid
@desc = desc
@nhr = opt[:nhr] # not human-readable?
@binary = opt[:binary] # binary encoding forced?
@re = opt[:re] # regular expression for parsing
@def = nil
instance_eval(&blk) if blk
end
def to_s
@oid
end
# Create a new Syntax object, given its description string
def self.from_def(str, &blk)
m = LDAPSyntaxDescription.match(str)
raise LDAP::ResultError::InvalidAttributeSyntax,
"Bad SyntaxTypeDescription #{str.inspect}" unless m
new(m[1], m[2], :nhr=>(m[3] == 'TRUE'), :binary=>(m[4] == 'TRUE'), &blk)
end
# Convert this object to its description string
def to_def
return @def if @def
ans = "( #@oid "
ans << "DESC '#@desc' " if @desc
# These are OpenLDAP extensions
ans << "X-BINARY-TRANSFER-REQUIRED 'TRUE' " if @binary
ans << "X-NOT-HUMAN-READABLE 'TRUE' " if @nhr
ans << ")"
@def = ans
end
# Return true or a MatchData object if the given value is allowed
# by this syntax
def match(val)
return true if @re.nil?
@re.match(value_to_s(val))
end
# Convert a value for this syntax into its canonical string representation
# (not yet used, but seemed like a good idea)
def value_to_s(val)
val.to_s
end
# Convert a string value for this syntax into a Ruby-like value
# (not yet used, but seemed like a good idea)
def value_from_s(val)
val
end
@@syntaxes = {}
# Add a new syntax definition
def self.add(*args, &blk)
s = new(*args, &blk)
@@syntaxes[s.oid] = s
end
# Find a Syntax object given an oid. If not known, return a new empty
# Syntax object associated with this oid.
def self.find(oid)
return oid if oid.nil? or oid.is_a?(LDAP::Server::Syntax)
return @@syntaxes[oid] if @@syntaxes[oid]
add(oid)
end
# Return all known syntax objects
def self.all_syntaxes
@@syntaxes.values.uniq
end
# Shared constants for regexp-based syntax parsers
KEYSTR = "[a-zA-Z][a-zA-Z0-9;-]*"
NUMERICOID = "( \\d[\\d.]+\\d )"
WOID = "\\s* ( #{KEYSTR} | \\d[\\d.]+\\d ) \\s*"
_WOID = "\\s* (?: #{KEYSTR} | \\d[\\d.]+\\d ) \\s*"
OIDS = "( #{_WOID} | \\s* \\( #{_WOID} (?: \\$ #{_WOID} )* \\) \\s* )"
_QDESCR = "\\s* ' #{KEYSTR} ' \\s*"
QDESCRS = "( #{_QDESCR} | \\s* \\( (?:#{_QDESCR})+ \\) \\s* )"
QDSTRING = "\\s* ' (.*?) ' \\s*"
NOIDLEN = "(\\d[\\d.]+\\d) (?: \\{ (\\d+) \\} )?"
ATTRIBUTEUSAGE = "(userApplications|directoryOperation|distributedOperation|dSAOperation)"
end
class Syntax
# These are the 'SHOULD' support syntaxes from RFC2252 section 6
AttributeTypeDescription =
add("1.3.6.1.4.1.1466.115.121.1.3", "Attribute Type Description", :re=>
%r! \A \s* \( \s*
#{NUMERICOID} \s*
(?: NAME #{QDESCRS} )?
(?: DESC #{QDSTRING} )?
( OBSOLETE \s* )?
(?: SUP #{WOID} )?
(?: EQUALITY #{WOID} )?
(?: ORDERING #{WOID} )?
(?: SUBSTR #{WOID} )?
(?: SYNTAX \s* #{NOIDLEN} \s* )? # capture 2
( SINGLE-VALUE \s* )?
( COLLECTIVE \s* )?
( NO-USER-MODIFICATION \s* )?
(?: USAGE \s* #{ATTRIBUTEUSAGE} )?
\s* \) \s* \z !xu)
add("1.3.6.1.4.1.1466.115.121.1.5", "Binary", :nhr=>true)
# FIXME: value_to_s should BER-encode the value??
add("1.3.6.1.4.1.1466.115.121.1.6", "Bit String", :re=>/\A'([01]*)'B\z/)
# FIXME: convert to FixNum?
add("1.3.6.1.4.1.1466.115.121.1.7", "Boolean", :re=>/\A(TRUE|FALSE)\z/) do
def self.value_to_s(v)
return v if v.is_a?(string)
v ? "TRUE" : "FALSE"
end
def self.value_from_s(v)
v.upcase == "TRUE"
end
end
add("1.3.6.1.4.1.1466.115.121.1.8", "Certificate", :binary=>true, :nhr=>true)
add("1.3.6.1.4.1.1466.115.121.1.9", "Certificate List", :binary=>true, :nhr=>true)
add("1.3.6.1.4.1.1466.115.121.1.10", "Certificate Pair", :binary=>true, :nhr=>true)
add("1.3.6.1.4.1.1466.115.121.1.11", "Country String", :re=>/\A[A-Z]{2}\z/i)
add("1.3.6.1.4.1.1466.115.121.1.12", "Distinguished Name")
# FIXME: validate DN?
add("1.3.6.1.4.1.1466.115.121.1.15", "Directory String")
# missed due to lack of interest: "DIT Content Rule Description"
add("1.3.6.1.4.1.1466.115.121.1.22", "Facsimile Telephone Number")
add(" 1.3.6.1.4.1.1466.115.121.1.23", "Fax", :nhr=>true)
add("1.3.6.1.4.1.1466.115.121.1.24", "Generalized Time")
# FIXME: Validate Generalized Time (find X.208) and convert to/from Ruby Time
add("1.3.6.1.4.1.1466.115.121.1.26", "IA5 String")
add("1.3.6.1.4.1.1466.115.121.1.27", "Integer", :re=>/\A\d+\z/) do
def self.value_from_s(v)
v.to_i
end
end
add("1.3.6.1.4.1.1466.115.121.1.28", "JPEG", :nhr=>true)
MatchingRuleDescription =
add("1.3.6.1.4.1.1466.115.121.1.30", "Matching Rule Description", :re=>
%r! \A \s* \( \s*
#{NUMERICOID} \s*
(?: NAME #{QDESCRS} )?
(?: DESC #{QDSTRING} )?
( OBSOLETE \s* )?
SYNTAX \s* #{NUMERICOID} \s*
\s* \) \s* \z !xu)
MatchingRuleUseDescription =
add("1.3.6.1.4.1.1466.115.121.1.31", "Matching Rule Use Description", :re=>
%r! \A \s* \( \s*
#{NUMERICOID} \s*
(?: NAME #{QDESCRS} )?
(?: DESC #{QDSTRING} )?
( OBSOLETE \s* )?
APPLIES \s* #{OIDS} \s*
\s* \) \s* \z !xu)
add("1.3.6.1.4.1.1466.115.121.1.33", "MHS OR Address")
add("1.3.6.1.4.1.1466.115.121.1.34", "Name And Optional UID")
# missed due to lack of interest: "Name Form Description"
add("1.3.6.1.4.1.1466.115.121.1.36", "Numeric String", :re=>/\A\d+\z/)
ObjectClassDescription =
add("1.3.6.1.4.1.1466.115.121.1.37", "Object Class Description", :re=>
%r! \A \s* \( \s*
#{NUMERICOID} \s*
(?: NAME #{QDESCRS} )?
(?: DESC #{QDSTRING} )?
( OBSOLETE \s* )?
(?: SUP #{OIDS} )?
(?: ( ABSTRACT|STRUCTURAL|AUXILIARY ) \s* )?
(?: MUST #{OIDS} )?
(?: MAY #{OIDS} )?
\s* \) \s* \z !xu)
add("1.3.6.1.4.1.1466.115.121.1.38", "OID", :re=>/\A#{WOID}\z/xu)
add("1.3.6.1.4.1.1466.115.121.1.39", "Other Mailbox")
add("1.3.6.1.4.1.1466.115.121.1.41", "Postal Address") do
def self.value_from_s(v)
v.split(/\$/)
end
def self.value_to_s(v)
return v.join("$") if v.is_a?(Array)
return v
end
end
add("1.3.6.1.4.1.1466.115.121.1.43", "Presentation Address")
add("1.3.6.1.4.1.1466.115.121.1.44", "Printable String")
add("1.3.6.1.4.1.1466.115.121.1.50", "Telephone Number")
add("1.3.6.1.4.1.1466.115.121.1.53", "UTC Time")
LDAPSyntaxDescription =
add("1.3.6.1.4.1.1466.115.121.1.54", "LDAP Syntax Description", :re=>
%r! \A \s* \( \s*
#{NUMERICOID} \s*
(?: DESC #{QDSTRING} )?
(?: X-BINARY-TRANSFER-REQUIRED \s* ' (TRUE|FALSE) ' \s* )?
(?: X-NOT-HUMAN-READABLE \s* ' (TRUE|FALSE) ' \s* )?
\s* \) \s* \z !xu)
# Missed due to lack of interest: "DIT Structure Rule Description"
# A few others from RFC2252 section 4.3.2
add("1.3.6.1.4.1.1466.115.121.1.4", "Audio", :nhr=>true)
add("1.3.6.1.4.1.1466.115.121.1.40", "Octet String")
add("1.3.6.1.4.1.1466.115.121.1.58", "Substring Assertion")
end
end # class Server
end # module LDAP