From 0fdbf17bf4204ac65e10f06bc994c53840a0dcd2 Mon Sep 17 00:00:00 2001 From: Julian Rother <julian@jrother.eu> Date: Fri, 26 Nov 2021 00:01:49 +0100 Subject: [PATCH] Template interface cleanup ([WILDCARD_VALUE] -> WILDCARD) --- docs/api.rst | 12 +++--------- ldapserver/entries.py | 20 ++++++++++++++------ tests/test_entries.py | 34 +++++++++++++++++----------------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 3284158..23ba4f4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -47,8 +47,9 @@ There are different kinds of entries: Object entries holding actual data objects .. autoclass:: ldapserver.SubschemaSubentry :members: -.. autodata:: ldapserver.WILDCARD_VALUE - :no-value: +.. py:data:: ldapserver.WILDCARD + + Special wildcard singleton for :class:`EntryTemplate` .. autoclass:: ldapserver.EntryTemplate :members: @@ -235,13 +236,6 @@ the diagnostic message. .. autoexception:: ldapserver.exceptions.LDAPAffectsMultipleDSAs .. autoexception:: ldapserver.exceptions.LDAPOther -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - .. _RFC 4513: https://tools.ietf.org/html/rfc4513 .. _RFC 4513 5.1.1.: https://tools.ietf.org/html/rfc4513#section-5.1.1 .. _RFC 4513 5.1.2.: https://tools.ietf.org/html/rfc4513#section-5.1.2 diff --git a/ldapserver/entries.py b/ldapserver/entries.py index 5a6c931..18bf7e7 100644 --- a/ldapserver/entries.py +++ b/ldapserver/entries.py @@ -4,7 +4,7 @@ import enum from . import ldap, exceptions from .dn import DN, RDN, RDNAssertion -__all__ = ['AttributeDict', 'Entry', 'ObjectEntry', 'RootDSE', 'WILDCARD_VALUE', 'EntryTemplate', 'SubschemaSubentry'] +__all__ = ['AttributeDict', 'Entry', 'ObjectEntry', 'RootDSE', 'WILDCARD', 'EntryTemplate', 'SubschemaSubentry'] class TypeKeysView(collections.abc.Set): def __init__(self, attributes): @@ -315,9 +315,10 @@ class RootDSE(Entry): class WildcardValue: pass -#: Special wildcard value for :class:`EntryTemplate` WILDCARD_VALUE = WildcardValue() +WILDCARD = (WILDCARD_VALUE,) + class TemplateFilterResult(enum.Enum): TRUE = enum.auto() FALSE = enum.auto() @@ -325,6 +326,13 @@ class TemplateFilterResult(enum.Enum): MAYBE_TRUE = enum.auto() class EntryTemplate(AttributeDict): + '''Utility class for dynamically generated object entries + + Set all entry-specific attributes to the special :data:`WILDCARD` value + and call :any:EntryTemplate.match_search` to decide if any entries based + on the template might match a SEARCH request. Call + :any:EntryTemplate.create_entry` with the actual values to create entries + based on a template.''' def __init__(self, schema, parent_dn, rdn_attribute, **attributes): super().__init__(schema, **attributes) self.parent_dn = DN(schema, parent_dn) @@ -507,7 +515,7 @@ class EntryTemplate(AttributeDict): Example: >>> subschema = SubschemaSubentry(schema.RFC4519_SCHEMA, 'cn=Subschema') - >>> template = subschema.EntryTemplate('ou=users,dc=example,dc=com', 'cn', objectclass=['person', 'top'], sn=[WILDCARD_VALUE]) + >>> template = subschema.EntryTemplate('ou=users,dc=example,dc=com', 'cn', objectclass=['person', 'top'], sn=WILDCARD) >>> template.extract_search_constraints('cn=test,ou=users,dc=example,dc=com', ldap.SearchScope.baseObject, ldap.FilterEqual('sn', b'foobar')) subschema.AttributeDict(cn=['test'], sn=['foobar']) @@ -527,13 +535,13 @@ class EntryTemplate(AttributeDict): :type rdn_value: any :rtype: Entry - Template attributes set to :any:`WILDCARD_VALUE` are stripped. Only - template attributes set to :any:`WILDCARD_VALUE` may be overwritten + Template attributes set to :any:`WILDCARD` are stripped. Only + template attributes set to :any:`WILDCARD` may be overwritten with `attributes`.''' obj = Entry(self.schema, DN(self.schema, self.parent_dn, **{self.rdn_attribute: rdn_value})) for key, values in attributes.items(): if WILDCARD_VALUE not in self[key]: - raise ValueError(f'Cannot set attribute "{key}" that is not set to [WILDCARD_VALUE] in the template') + raise ValueError(f'Cannot set attribute "{key}" that is not set to WILDCARD in the template') obj[key] = values for attribute_type, values in self.items(): if WILDCARD_VALUE not in values: diff --git a/tests/test_entries.py b/tests/test_entries.py index e1d7529..5f87436 100644 --- a/tests/test_entries.py +++ b/tests/test_entries.py @@ -2,7 +2,7 @@ import unittest import datetime import ldapserver -from ldapserver import DN, AttributeDict, ObjectEntry, RootDSE, SubschemaSubentry, EntryTemplate, WILDCARD_VALUE, ldap +from ldapserver import DN, AttributeDict, ObjectEntry, RootDSE, SubschemaSubentry, EntryTemplate, WILDCARD, ldap schema = ldapserver.schema.RFC4519_SCHEMA @@ -315,7 +315,7 @@ class TestObjectEntry(unittest.TestCase): obj = ObjectEntry(schema, 'cn=foo,dc=example,dc=com', cn=['foo', 'test'], uid=['foobar'], objectclass=['top']) dn = 'cn=foo,dc=example,dc=com' scope = ldap.SearchScope.baseObject - obj = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar']) + obj = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar']) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -403,7 +403,7 @@ class TestEntryTemplate(unittest.TestCase): obj = EntryTemplate(schema, 'ou=users,dc=example,dc=com', 'uid', cn=['foo', 'bar'], uid=[]) def test_match_search_dn(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) true_filter = ldap.FilterPresent('objectClass') scope = ldap.SearchScope.baseObject @@ -425,7 +425,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertFalse(template.match_search('cn=test,cn=foo,dc=example,dc=com', scope, true_filter)) def test_match_search_filter_present(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -439,7 +439,7 @@ class TestEntryTemplate(unittest.TestCase): # We verify in ..._filter_not that Undefined/Maybe are not just False/True def test_match_search_filter_not(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # Not True = False @@ -452,7 +452,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterPresent('cn')))) def test_match_search_filter_and(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree true = ldap.FilterPresent('objectclass') @@ -499,7 +499,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertFalse(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterAnd([undefined, maybe])))) def test_match_search_filter_or(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree true = ldap.FilterPresent('objectclass') @@ -546,7 +546,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterOr([undefined, maybe])))) def test_match_search_filter_equal(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -573,7 +573,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterEqual('name', b'bar')))) # subtype def test_match_search_filter_substr(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar', 'test']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar', 'test']) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -594,7 +594,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterSubstrings('cn', [ldap.InitialSubstring(b'foo')])))) def test_match_search_filter_le(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], createTimestamp=[datetime.datetime(1994, 12, 16, 10, 32, tzinfo=datetime.timezone.utc)], modifyTimestamp=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, createTimestamp=[datetime.datetime(1994, 12, 16, 10, 32, tzinfo=datetime.timezone.utc)], modifyTimestamp=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -619,7 +619,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterLessOrEqual('modifyTimestamp', b'199412161032Z')))) def test_match_search_filter_ge(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], createTimestamp=[datetime.datetime(1994, 12, 16, 10, 32, tzinfo=datetime.timezone.utc)], modifyTimestamp=[WILDCARD_VALUE]) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, createTimestamp=[datetime.datetime(1994, 12, 16, 10, 32, tzinfo=datetime.timezone.utc)], modifyTimestamp=WILDCARD) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -644,7 +644,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterGreaterOrEqual('modifyTimestamp', b'199412161032Z')))) def test_match_search_filter_extensible_attribute_type(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar']) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -671,7 +671,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterExtensibleMatch('caseIgnoreMatch', 'cn', b'Foobar', False)))) def test_match_search_filter_extensible_no_attribute_type(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar']) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -694,7 +694,7 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterExtensibleMatch('caseIgnoreMatch', None, b'foo', False)))) def test_match_search_filter_extensible_dn(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar']) dn = 'dc=example,dc=com' scope = ldap.SearchScope.wholeSubtree # True @@ -719,14 +719,14 @@ class TestEntryTemplate(unittest.TestCase): self.assertTrue(template.match_search(dn, scope, ldap.FilterNot(ldap.FilterExtensibleMatch('caseIgnoreMatch', 'cn', b'foo', True)))) def test_extract_search_constraints(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], uid=['foobar']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, uid=['foobar']) self.assertEqual(dict(template.extract_search_constraints('dc=exapmle,dc=com', ldap.SearchScope.wholeSubtree, ldap.FilterEqual('cn', b'foo')).items()), {'cn': ['foo']}) self.assertEqual(dict(template.extract_search_constraints('dc=exapmle,dc=com', ldap.SearchScope.wholeSubtree, ldap.FilterAnd([ldap.FilterEqual('objectclass', b'top'), ldap.FilterEqual('cn', b'foo')])).items()), {'cn': ['foo'], 'objectClass': ['top']}) self.assertEqual(dict(template.extract_search_constraints('cn=foo,dc=example,dc=com', ldap.SearchScope.baseObject, ldap.FilterPresent('objectClass')).items()), {'cn': ['foo']}) self.assertEqual(dict(template.extract_search_constraints('dc=example,dc=com', ldap.SearchScope.baseObject, ldap.FilterPresent('objectClass')).items()), {}) def test_create_entry(self): - template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE], c=[WILDCARD_VALUE], uid=['foobar']) + template = EntryTemplate(schema, 'dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD, c=WILDCARD, uid=['foobar']) obj = template.create_entry('foo', cn=['foo', 'bar'], c=['DE']) self.assertEqual(obj.dn, DN(schema, 'cn=foo,dc=example,dc=com')) self.assertEqual(dict(obj.items()), {'cn': ['foo', 'bar'], 'uid': ['foobar'], 'c': ['DE'], 'objectClass': ['top']}) @@ -764,7 +764,7 @@ class TestSubschemaSubentry(unittest.TestCase): self.assertIsInstance(rootdse, RootDSE) self.assertIs(rootdse.schema, subschema.schema) self.assertEqual(rootdse['cn'], ['foo']) - template = subschema.EntryTemplate('dc=example,dc=com', 'cn', objectclass=['top'], cn=[WILDCARD_VALUE]) + template = subschema.EntryTemplate('dc=example,dc=com', 'cn', objectclass=['top'], cn=WILDCARD) self.assertIsInstance(template, EntryTemplate) self.assertIs(template.schema, subschema.schema) self.assertEqual(template['subschemaSubentry'], [DN(schema, 'cn=Subschema')]) -- GitLab