Skip to content

Commit

Permalink
Merge pull request #152 – bytes mode for default arguments
Browse files Browse the repository at this point in the history
With `bytes_mode=True`, all arguments must be bytes.
However some functions had default keyword arguments of type `str` (`unicode`).
One example is `search_ext(..., filterstr='(objectClass=*), ...')`.

Here is a fix.

https://github.com/python-ldap/python-ldap/pull/152
  • Loading branch information
Petr Viktorin authored and GitHub committed Jan 10, 2018
2 parents 6e75fc4 + f8f8c9c commit 91438fd
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 29 deletions.
3 changes: 3 additions & 0 deletions Doc/reference/ldap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,10 @@ and wait for and return with the server's result, or with
or :py:meth:`search_ext_s()` (client-side search limit). If non-zero
not more than *sizelimit* results are returned by the server.

.. versionchanged:: 3.0

``filterstr=None`` is equivalent to ``filterstr='(objectClass=*)'``.


.. py:method:: LDAPObject.start_tls_s() -> None
Expand Down
78 changes: 58 additions & 20 deletions Lib/ldap/ldapobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ def result4(self,msgid=ldap.RES_ANY,all=1,timeout=None,add_ctrls=0,add_intermedi
resp_data = self._bytesify_results(resp_data, with_ctrls=add_ctrls)
return resp_type, resp_data, resp_msgid, decoded_resp_ctrls, resp_name, resp_value

def search_ext(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
def search_ext(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
"""
search(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]]) -> int
search_s(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]])
Expand Down Expand Up @@ -793,12 +793,24 @@ def search_ext(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrson
The amount of search results retrieved can be limited with the
sizelimit parameter if non-zero.
"""

if PY2:
base = self._bytesify_input('base', base)
filterstr = self._bytesify_input('filterstr', filterstr)
if filterstr is None:
# workaround for default argument,
# see https://github.com/python-ldap/python-ldap/issues/147
if self.bytes_mode:
filterstr = b'(objectClass=*)'
else:
filterstr = u'(objectClass=*)'
else:
filterstr = self._bytesify_input('filterstr', filterstr)
if attrlist is not None:
attrlist = tuple(self._bytesify_input('attrlist', a)
for a in attrlist)
else:
if filterstr is None:
filterstr = '(objectClass=*)'
return self._ldap_call(
self._l.search_ext,
base,scope,filterstr,
Expand All @@ -808,17 +820,17 @@ def search_ext(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrson
timeout,sizelimit,
)

def search_ext_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
def search_ext_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit)
return self.result(msgid,all=1,timeout=timeout)[1]

def search(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0):
def search(self,base,scope,filterstr=None,attrlist=None,attrsonly=0):
return self.search_ext(base,scope,filterstr,attrlist,attrsonly,None,None)

def search_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0):
def search_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0):
return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout)

def search_st(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,timeout=-1):
def search_st(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,timeout=-1):
return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout)

def start_tls_s(self):
Expand Down Expand Up @@ -885,7 +897,7 @@ def set_option(self,option,invalue):
invalue = RequestControlTuples(invalue)
return self._ldap_call(self._l.set_option,option,invalue)

def search_subschemasubentry_s(self,dn=''):
def search_subschemasubentry_s(self,dn=None):
"""
Returns the distinguished name of the sub schema sub entry
for a part of a DIT specified by dn.
Expand All @@ -895,9 +907,17 @@ def search_subschemasubentry_s(self,dn=''):
Returns: None or text/bytes depending on bytes_mode.
"""
if self.bytes_mode:
empty_dn = b''
attrname = b'subschemaSubentry'
else:
empty_dn = u''
attrname = u'subschemaSubentry'
if dn is None:
dn = empty_dn
try:
r = self.search_s(
dn,ldap.SCOPE_BASE,'(objectClass=*)',['subschemaSubentry']
dn,ldap.SCOPE_BASE,None,[attrname]
)
except (ldap.NO_SUCH_OBJECT,ldap.NO_SUCH_ATTRIBUTE,ldap.INSUFFICIENT_ACCESS):
r = []
Expand All @@ -906,11 +926,11 @@ def search_subschemasubentry_s(self,dn=''):
try:
if r:
e = ldap.cidict.cidict(r[0][1])
search_subschemasubentry_dn = e.get('subschemaSubentry',[None])[0]
search_subschemasubentry_dn = e.get(attrname,[None])[0]
if search_subschemasubentry_dn is None:
if dn:
# Try to find sub schema sub entry in root DSE
return self.search_subschemasubentry_s(dn='')
return self.search_subschemasubentry_s(dn=empty_dn)
else:
# If dn was already root DSE we can return here
return None
Expand All @@ -930,7 +950,7 @@ def read_s(self,dn,filterstr=None,attrlist=None,serverctrls=None,clientctrls=Non
r = self.search_ext_s(
dn,
ldap.SCOPE_BASE,
filterstr or '(objectClass=*)',
filterstr,
attrlist=attrlist,
serverctrls=serverctrls,
clientctrls=clientctrls,
Expand All @@ -945,26 +965,34 @@ def read_subschemasubentry_s(self,subschemasubentry_dn,attrs=None):
"""
Returns the sub schema sub entry's data
"""
if self.bytes_mode:
filterstr = b'(objectClass=subschema)'
if attrs is None:
attrs = [attr.encode('utf-8') for attr in SCHEMA_ATTRS]
else:
filterstr = u'(objectClass=subschema)'
if attrs is None:
attrs = SCHEMA_ATTRS
try:
subschemasubentry = self.read_s(
subschemasubentry_dn,
filterstr='(objectClass=subschema)',
attrlist=attrs or SCHEMA_ATTRS
filterstr=filterstr,
attrlist=attrs
)
except ldap.NO_SUCH_OBJECT:
return None
else:
return subschemasubentry

def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1):
def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1):
"""
Returns a unique entry, raises exception if not unique
"""
r = self.search_ext_s(
base,
scope,
filterstr,
attrlist=attrlist or ['*'],
attrlist=attrlist,
attrsonly=attrsonly,
serverctrls=serverctrls,
clientctrls=clientctrls,
Expand All @@ -975,14 +1003,20 @@ def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr='(objectClass
raise NO_UNIQUE_ENTRY('No or non-unique search result for %s' % (repr(filterstr)))
return r[0]

def read_rootdse_s(self, filterstr='(objectClass=*)', attrlist=None):
def read_rootdse_s(self, filterstr=None, attrlist=None):
"""
convenience wrapper around read_s() for reading rootDSE
"""
if self.bytes_mode:
base = b''
attrlist = attrlist or [b'*', b'+']
else:
base = u''
attrlist = attrlist or [u'*', u'+']
ldap_rootdse = self.read_s(
'',
base,
filterstr=filterstr,
attrlist=attrlist or ['*', '+'],
attrlist=attrlist,
)
return ldap_rootdse # read_rootdse_s()

Expand All @@ -991,9 +1025,13 @@ def get_naming_contexts(self):
returns all attribute values of namingContexts in rootDSE
if namingContexts is not present (not readable) then empty list is returned
"""
if self.bytes_mode:
name = b'namingContexts'
else:
name = u'namingContexts'
return self.read_rootdse_s(
attrlist=['namingContexts']
).get('namingContexts', [])
attrlist=[name]
).get(name, [])


class ReconnectLDAPObject(SimpleLDAPObject):
Expand Down
16 changes: 8 additions & 8 deletions Lib/ldap/schema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class ObjectClass(SchemaElement):
This list of strings contains NAMEs or OIDs of object classes
this object class is derived from
"""
schema_attribute = 'objectClasses'
schema_attribute = u'objectClasses'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down Expand Up @@ -225,7 +225,7 @@ class AttributeType(SchemaElement):
This list of strings contains NAMEs or OIDs of attribute types
this attribute type is derived from
"""
schema_attribute = 'attributeTypes'
schema_attribute = u'attributeTypes'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down Expand Up @@ -319,7 +319,7 @@ class LDAPSyntax(SchemaElement):
Integer flag (0 or 1) indicating whether the attribute type is marked
as not human-readable (X-NOT-HUMAN-READABLE)
"""
schema_attribute = 'ldapSyntaxes'
schema_attribute = u'ldapSyntaxes'
token_defaults = {
'DESC':(None,),
'X-NOT-HUMAN-READABLE':(None,),
Expand Down Expand Up @@ -367,7 +367,7 @@ class MatchingRule(SchemaElement):
syntax
String contains OID of the LDAP syntax this matching rule is usable with
"""
schema_attribute = 'matchingRules'
schema_attribute = u'matchingRules'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down Expand Up @@ -413,7 +413,7 @@ class MatchingRuleUse(SchemaElement):
This list of strings contains NAMEs or OIDs of attribute types
for which this matching rule is used
"""
schema_attribute = 'matchingRuleUse'
schema_attribute = u'matchingRuleUse'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down Expand Up @@ -470,7 +470,7 @@ class DITContentRule(SchemaElement):
This list of strings contains NAMEs or OIDs of attributes which
may not be present in an entry of the object class
"""
schema_attribute = 'dITContentRules'
schema_attribute = u'dITContentRules'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down Expand Up @@ -527,7 +527,7 @@ class DITStructureRule(SchemaElement):
List of strings with NAMEs or OIDs of allowed structural object classes
of superior entries in the DIT
"""
schema_attribute = 'dITStructureRules'
schema_attribute = u'dITStructureRules'

token_defaults = {
'NAME':(()),
Expand Down Expand Up @@ -591,7 +591,7 @@ class NameForm(SchemaElement):
This list of strings contains NAMEs or OIDs of additional attributes
an RDN may contain
"""
schema_attribute = 'nameForms'
schema_attribute = u'nameForms'
token_defaults = {
'NAME':(()),
'DESC':(None,),
Expand Down
Loading

0 comments on commit 91438fd

Please sign in to comment.