Skip to content
Snippets Groups Projects
Commit d4b16b9a authored by Julian Rother's avatar Julian Rother
Browse files

Added core schema definitions and integrated schema in SimpleRequestHandler

parent f516bc02
No related branches found
No related tags found
No related merge requests found
......@@ -174,15 +174,25 @@ class RootDSE(BaseDirectory, AttributeDict):
return [('', attrs)]
class Subschema(BaseDirectory, AttributeDict):
def __init__(self, *args, dn='cn=Subschema', **kwargs):
def __init__(self, *args,
dn='cn=Subschema',
structuralobjectclass='subentry',
objectclass=('top', 'subschema', 'extensibleObject'),
subtreespecification='{ }',
ldapsyntaxes=SYNTAXES,
matchingrules=MATCHING_RULES,
objectclasses=OBJECT_CLASSES,
attributetypes=ATTRIBUTE_TYPES,
**kwargs):
super().__init__(*args, **kwargs)
self.dn = DN(dn)
for key, value in self.dn[0]:
self.setdefault(key, [encode_attribute(value)])
self.setdefault('objectClass', ['top', 'subentry', 'subschema', 'extensibleObject'])
#if 'subschema' not in self['objectClass']:
# self['objectClass'].append('subschema')
self.setdefault('structuralObjectClass', ['subentry'])
self['structuralObjectClass'] = [structuralobjectclass]
self['objectClass'] = list(objectclass)
self['subtreeSpecification'] = [subtreespecification]
self['ldapSyntaxes'] = list(ldapsyntaxes)
self['matchingRules'] = list(matchingrules)
self['objectClasses'] = list(objectclasses)
self['attributeTypes'] = list(attributetypes)
def search(self, baseobj, scope, filter):
if DN(baseobj) != self.dn or scope != SearchScope.baseObject:
......@@ -190,10 +200,34 @@ class Subschema(BaseDirectory, AttributeDict):
if not isinstance(filter, FilterEqual):
return []
if filter.attribute.lower() != 'objectclass' or filter.value.lower() != b'subschema':
print('filter mismatch2')
return []
return [(str(self.dn), {key: [encode_attribute(value) for value in values] for key, values in self.items()})]
def load_schema(self, definition):
'''Load objectClasses and attributeTypes from OpenLDAP-compatible schema definition
:param definition: Schema definition text (as found in .schema files, not LDIF!)
:type definition: str
'''
statement = ''
for line in definition.split('\n'):
if line.startswith('#') or not line:
continue
if line[0] not in [' ', '\t'] and statement:
key, value = statement.split(maxsplit=1)
if key.lower() == 'attributetype':
if value not in self['attributeTypes']:
self['attributeTypes'].append(value)
elif key.lower() == 'objectclass':
if value not in self['objectClasses']:
self['objectClasses'].append(value)
elif key.lower() == 'objectidentifier':
pass
else:
raise ValueError('Unknown key "%s"'%key)
statement = ''
statement += line.lstrip() + ' '
def eval_ldap_filter(obj, expr):
'''Return whether LDAP filter expression matches attribute dictionary'''
if expr is True:
......
......@@ -494,3 +494,122 @@ class PasswdModifyResponseValue(asn1.Sequence):
sequence_fields = [
(asn1.retag(asn1.OctetString, (2, False, 0)), 'genPasswd', None, True),
]
# Core Schema Definitions
SYNTAXES = (
# RFC 4517
"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )",
"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )",
"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )",
"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'DN' )",
"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )",
"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number')",
"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' )",
"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )",
"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )",
"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'INTEGER' )",
"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' )",
"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )",
"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )",
"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )",
"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )",
"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )",
"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )",
"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )",
"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )",
# RFC 3672 (Subentries in LDAP)
"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )",
)
MATCHING_RULES = (
# RFC 4517
"( 2.5.13.16 NAME 'bitStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
"( 2.5.13.13 NAME 'booleanMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )",
"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
"( 2.5.13.5 NAME 'caseExactMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.6 NAME 'caseExactOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.7 NAME 'caseExactSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 2.5.13.11 NAME 'caseIgnoreListMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )",
"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 2.5.13.2 NAME 'caseIgnoreMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 2.5.13.31 NAME 'directoryStringFirstComponentMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.1 NAME 'distinguishedNameMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
"( 2.5.13.27 NAME 'generalizedTimeMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
"( 2.5.13.29 NAME 'integerFirstComponentMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
"( 2.5.13.14 NAME 'integerMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
"( 2.5.13.15 NAME 'integerOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
"( 2.5.13.33 NAME 'keywordMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
"( 2.5.13.8 NAME 'numericStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
"( 2.5.13.9 NAME 'numericStringOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
"( 2.5.13.10 NAME 'numericStringSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
"( 2.5.13.0 NAME 'objectIdentifierMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
"( 2.5.13.17 NAME 'octetStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
"( 2.5.13.18 NAME 'octetStringOrderingMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
"( 2.5.13.20 NAME 'telephoneNumberMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )",
"( 2.5.13.23 NAME 'uniqueMemberMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )",
"( 2.5.13.32 NAME 'wordMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
)
OBJECT_CLASSES = (
# RFC 45112
"( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass )",
"( 2.5.6.1 NAME 'alias' SUP top STRUCTURAL MUST aliasedObjectName )",
"( 2.5.20.1 NAME 'subschema' AUXILIARY MAY ( dITStructureRules $ nameForms $ ditContentRules $ objectClasses $ attributeTypes $ matchingRules $ matchingRuleUse ) )",
"( 1.3.6.1.4.1.1466.101.120.111 NAME 'extensibleObject' SUP top AUXILIARY )",
# RFC 3672 (Subentries in LDAP)
"( 2.5.17.0 NAME 'subentry' SUP top STRUCTURAL MUST ( cn $ subtreeSpecification ) )",
)
ATTRIBUTE_TYPES = (
# RFC 45112
"( 2.5.4.1 NAME 'aliasedObjectName' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )",
"( 2.5.4.0 NAME 'objectClass' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
"( 2.5.18.3 NAME 'creatorsName' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.18.1 NAME 'createTimestamp' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.18.4 NAME 'modifiersName' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.18.2 NAME 'modifyTimestamp' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.21.9 NAME 'structuralObjectClass' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.21.10 NAME 'governingStructureRule' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.18.10 NAME 'subschemaSubentry' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
"( 2.5.21.6 NAME 'objectClasses' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.37 USAGE directoryOperation )",
"( 2.5.21.5 NAME 'attributeTypes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.3 USAGE directoryOperation )",
"( 2.5.21.4 NAME 'matchingRules' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.30 USAGE directoryOperation )",
"( 2.5.21.8 NAME 'matchingRuleUse' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.31 USAGE directoryOperation )",
"( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )",
"( 2.5.21.2 NAME 'dITContentRules' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.16 USAGE directoryOperation )",
"( 2.5.21.1 NAME 'dITStructureRules' EQUALITY integerFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.17 USAGE directoryOperation )",
"( 2.5.21.7 NAME 'nameForms' EQUALITY objectIdentifierFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.35 USAGE directoryOperation )",
"( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 USAGE dSAOperation )",
"( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE dSAOperation )",
"( 1.3.6.1.4.1.1466.101.120.13 NAME 'supportedControl' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )",
"( 1.3.6.1.4.1.1466.101.120.7 NAME 'supportedExtension' SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )",
"( 1.3.6.1.4.1.4203.1.3.5 NAME 'supportedFeatures' EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )",
"( 1.3.6.1.4.1.1466.101.120.15 NAME 'supportedLDAPVersion' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 USAGE dSAOperation )",
"( 1.3.6.1.4.1.1466.101.120.14 NAME 'supportedSASLMechanisms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE dSAOperation )",
# RFC 3672 (Subentries in LDAP)
"( 2.5.18.5 NAME 'administrativeRole' EQUALITY objectIdentifierMatch USAGE directoryOperation SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
"( 2.5.18.6 NAME 'subtreeSpecification' SINGLE-VALUE USAGE directoryOperation SYNTAX 1.3.6.1.4.1.1466.115.121.1.45 )",
)
This diff is collapsed.
......@@ -7,7 +7,7 @@ from socketserver import BaseRequestHandler
from .ldap import *
from .asn1 import IncompleteBERError
from .directory import RootDSE, BaseDirectory
from .directory import RootDSE, Subschema, BaseDirectory
from .exceptions import *
def decode_msg(shallowmsg):
......@@ -159,6 +159,20 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
mechansims. Attributes can be accessed in a dict-like fashion.
'''
subschema = Subschema()
'''
.. py:attribute:: subschema
Special single-object :any:`BaseDirectory` that describes the schema.
Per default the subschema includes standard syntaxes, standard matching
rules and objectclasses/attributetypes for the rootdse and subschema.
It does not include objectclasses/attributetypes for actual data
(e.g. users and groups). See :any:`Subschema` for details.
If `subschema` is not `None`, the subschemaSubentry attribute is
automatically added to all results returned by :any:`do_search`.
'''
def setup(self):
super().setup()
self.rootdse = RootDSE()
......@@ -416,15 +430,17 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
def handle_search(self, op, controls=None):
for dn, attributes in self.do_search(op.baseObject, op.scope, op.filter):
attributes = [PartialAttribute(name, values) for name, values in attributes.items()]
yield SearchResultEntry(dn, attributes)
pattributes = [PartialAttribute(name, values) for name, values in attributes.items()]
if 'subschemaSubentry' not in attributes and self.subschema is not None:
pattributes.append(PartialAttribute('subschemaSubentry', [bytes(self.subschema.dn)]))
yield SearchResultEntry(dn, pattributes)
yield SearchResultDone(LDAPResultCode.success)
def do_search(self, baseobj, scope, filter):
'''Do LDAP SEARCH operation
:param baseobj: Distinguished name of the LDAP entry relative to which the search is
to be performed
:param baseobj: Distinguished name of the LDAP entry relative to which the
search is to be performed
:type baseobj: str
:param scope: Search scope
:type scope: SearchScope
......@@ -435,8 +451,10 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
:returns: Iterable of dn, attributes tuples
The default implementation returns matching objects from the root dse.'''
return self.rootdse.search(baseobj, scope, filter)
The default implementation returns matching objects from the root dse and
the subschema.'''
yield from self.rootdse.search(baseobj, scope, filter)
yield from self.subschema.search(baseobj, scope, filter)
def handle_unbind(self, op, controls=None):
reject_critical_controls(controls)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment