Codebase list python-ldap / fa03454
New upstream version 3.0.0~b4 W. van den Akker 6 years ago
21 changed file(s) with 309 addition(s) and 151 deletion(s). Raw diff Collapse all Expand all
0 ----------------------------------------------------------------
1 Released 3.0.0b4 2018-01-10
2
3 Changes since 3.0.0b3:
4
5 Removed support for Python 3.3, which reached its end-of-life 2017-09-29.
6
7 Lib/
8 * Make default argument values work under bytes_mode
9 * Update use of map() to use list/set comprehensions instead
10
11 Test/
12 * Refactor syncrepl tests to run with bytes_mode
13
14 Doc/
15 * Document all_records attribute of LDIFRecordList
16
17
018 ----------------------------------------------------------------
119 Released 3.0.0b3 2017-12-20
220
213213 * Release the ``sdist`` on PyPI.
214214 * Announce the release on the mailing list.
215215 Mention the Git hash.
216 * Add the release's log from ``CHANGES`` on the `GitHub release page`_.
217
218 .. _GitHub release page: https://github.com/python-ldap/python-ldap/releases
119119 The following software packages are required to be installed
120120 on the local system when building python-ldap:
121121
122 - `Python`_ version 2.7, or 3.3 or later including its development files
122 - `Python`_ version 2.7, or 3.4 or later including its development files
123123 - C compiler corresponding to your Python version (on Linux, it is usually ``gcc``)
124124 - `OpenLDAP`_ client libs version 2.4.11 or later;
125125 it is not possible and not supported to build with prior versions.
999999 *serverctrls* and *clientctrls* like described in section :ref:`ldap-controls`.
10001000
10011001
1002 .. py:method:: LDAPObject.simple_bind([who='' [, cred='' [, serverctrls=None [, clientctrls=None]]]]) -> int
1003
1004 .. py:method:: LDAPObject.simple_bind_s([who='' [, cred='' [, serverctrls=None [, clientctrls=None]]]]) -> None
1002 .. py:method:: LDAPObject.simple_bind([who=None [, cred=None [, serverctrls=None [, clientctrls=None]]]]) -> int
1003
1004 .. py:method:: LDAPObject.simple_bind_s([who=None [, cred=None [, serverctrls=None [, clientctrls=None]]]]) -> None
10051005
10061006 After an LDAP object is created, and before any other operations can be
10071007 attempted over the connection, a bind operation must be performed.
10141014
10151015 *serverctrls* and *clientctrls* like described in section :ref:`ldap-controls`.
10161016
1017 .. versionchanged:: 3.0
1018
1019 :meth:`~LDAPObject.simple_bind` and :meth:`~LDAPObject.simple_bind_s`
1020 now accept ``None`` for *who* and *cred*, too.
1021
10171022
10181023 .. py:method:: LDAPObject.search(base, scope [,filterstr='(objectClass=*)' [, attrlist=None [, attrsonly=0]]]) ->int
10191024
10671072 or :py:meth:`search_ext_s()` (client-side search limit). If non-zero
10681073 not more than *sizelimit* results are returned by the server.
10691074
1070
1075 .. versionchanged:: 3.0
1076
1077 ``filterstr=None`` is equivalent to ``filterstr='(objectClass=*)'``.
1078
10711079
10721080 .. py:method:: LDAPObject.start_tls_s() -> None
10731081
107107 for deref_res in decodedValue:
108108 deref_attr,deref_val,deref_vals = deref_res[0],deref_res[1],deref_res[2]
109109 partial_attrs_dict = {
110 str(tv[0]): map(str,tv[1])
110 str(tv[0]): [str(v) for v in tv[1]]
111111 for tv in deref_vals or []
112112 }
113113 try:
5353 List or tuple of assertion values. Length must match
5454 count of %s in filter_template.
5555 """
56 return filter_template % (tuple(map(escape_filter_chars,assertion_values)))
56 return filter_template % tuple(escape_filter_chars(v) for v in assertion_values)
5757
5858
5959 def time_span_filter(
137137 Applies escape_func() to all items of `args' and returns a string based
138138 on format string `s'.
139139 """
140 escape_args = map(escape_func,args)
141 return s % tuple(escape_args)
140 return s % tuple(escape_func(v) for v in args)
142141
143142
144143 def strf_secs(secs):
405405 def add_s(self,dn,modlist):
406406 return self.add_ext_s(dn,modlist,None,None)
407407
408 def simple_bind(self,who='',cred='',serverctrls=None,clientctrls=None):
408 def simple_bind(self,who=None,cred=None,serverctrls=None,clientctrls=None):
409409 """
410410 simple_bind([who='' [,cred='']]) -> int
411411 """
414414 cred = self._bytesify_input('cred', cred)
415415 return self._ldap_call(self._l.simple_bind,who,cred,RequestControlTuples(serverctrls),RequestControlTuples(clientctrls))
416416
417 def simple_bind_s(self,who='',cred='',serverctrls=None,clientctrls=None):
417 def simple_bind_s(self,who=None,cred=None,serverctrls=None,clientctrls=None):
418418 """
419419 simple_bind_s([who='' [,cred='']]) -> 4-tuple
420420 """
747747 resp_data = self._bytesify_results(resp_data, with_ctrls=add_ctrls)
748748 return resp_type, resp_data, resp_msgid, decoded_resp_ctrls, resp_name, resp_value
749749
750 def search_ext(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
750 def search_ext(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
751751 """
752752 search(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]]) -> int
753753 search_s(base, scope [,filterstr='(objectClass=*)' [,attrlist=None [,attrsonly=0]]])
792792 The amount of search results retrieved can be limited with the
793793 sizelimit parameter if non-zero.
794794 """
795
795796 if PY2:
796797 base = self._bytesify_input('base', base)
797 filterstr = self._bytesify_input('filterstr', filterstr)
798 if filterstr is None:
799 # workaround for default argument,
800 # see https://github.com/python-ldap/python-ldap/issues/147
801 if self.bytes_mode:
802 filterstr = b'(objectClass=*)'
803 else:
804 filterstr = u'(objectClass=*)'
805 else:
806 filterstr = self._bytesify_input('filterstr', filterstr)
798807 if attrlist is not None:
799808 attrlist = tuple(self._bytesify_input('attrlist', a)
800809 for a in attrlist)
810 else:
811 if filterstr is None:
812 filterstr = '(objectClass=*)'
801813 return self._ldap_call(
802814 self._l.search_ext,
803815 base,scope,filterstr,
807819 timeout,sizelimit,
808820 )
809821
810 def search_ext_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
822 def search_ext_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1,sizelimit=0):
811823 msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit)
812824 return self.result(msgid,all=1,timeout=timeout)[1]
813825
814 def search(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0):
826 def search(self,base,scope,filterstr=None,attrlist=None,attrsonly=0):
815827 return self.search_ext(base,scope,filterstr,attrlist,attrsonly,None,None)
816828
817 def search_s(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0):
829 def search_s(self,base,scope,filterstr=None,attrlist=None,attrsonly=0):
818830 return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout)
819831
820 def search_st(self,base,scope,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,timeout=-1):
832 def search_st(self,base,scope,filterstr=None,attrlist=None,attrsonly=0,timeout=-1):
821833 return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout)
822834
823835 def start_tls_s(self):
884896 invalue = RequestControlTuples(invalue)
885897 return self._ldap_call(self._l.set_option,option,invalue)
886898
887 def search_subschemasubentry_s(self,dn=''):
899 def search_subschemasubentry_s(self,dn=None):
888900 """
889901 Returns the distinguished name of the sub schema sub entry
890902 for a part of a DIT specified by dn.
894906
895907 Returns: None or text/bytes depending on bytes_mode.
896908 """
909 if self.bytes_mode:
910 empty_dn = b''
911 attrname = b'subschemaSubentry'
912 else:
913 empty_dn = u''
914 attrname = u'subschemaSubentry'
915 if dn is None:
916 dn = empty_dn
897917 try:
898918 r = self.search_s(
899 dn,ldap.SCOPE_BASE,'(objectClass=*)',['subschemaSubentry']
919 dn,ldap.SCOPE_BASE,None,[attrname]
900920 )
901921 except (ldap.NO_SUCH_OBJECT,ldap.NO_SUCH_ATTRIBUTE,ldap.INSUFFICIENT_ACCESS):
902922 r = []
905925 try:
906926 if r:
907927 e = ldap.cidict.cidict(r[0][1])
908 search_subschemasubentry_dn = e.get('subschemaSubentry',[None])[0]
928 search_subschemasubentry_dn = e.get(attrname,[None])[0]
909929 if search_subschemasubentry_dn is None:
910930 if dn:
911931 # Try to find sub schema sub entry in root DSE
912 return self.search_subschemasubentry_s(dn='')
932 return self.search_subschemasubentry_s(dn=empty_dn)
913933 else:
914934 # If dn was already root DSE we can return here
915935 return None
929949 r = self.search_ext_s(
930950 dn,
931951 ldap.SCOPE_BASE,
932 filterstr or '(objectClass=*)',
952 filterstr,
933953 attrlist=attrlist,
934954 serverctrls=serverctrls,
935955 clientctrls=clientctrls,
944964 """
945965 Returns the sub schema sub entry's data
946966 """
967 if self.bytes_mode:
968 filterstr = b'(objectClass=subschema)'
969 if attrs is None:
970 attrs = [attr.encode('utf-8') for attr in SCHEMA_ATTRS]
971 else:
972 filterstr = u'(objectClass=subschema)'
973 if attrs is None:
974 attrs = SCHEMA_ATTRS
947975 try:
948976 subschemasubentry = self.read_s(
949977 subschemasubentry_dn,
950 filterstr='(objectClass=subschema)',
951 attrlist=attrs or SCHEMA_ATTRS
978 filterstr=filterstr,
979 attrlist=attrs
952980 )
953981 except ldap.NO_SUCH_OBJECT:
954982 return None
955983 else:
956984 return subschemasubentry
957985
958 def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr='(objectClass=*)',attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1):
986 def find_unique_entry(self,base,scope=ldap.SCOPE_SUBTREE,filterstr=None,attrlist=None,attrsonly=0,serverctrls=None,clientctrls=None,timeout=-1):
959987 """
960988 Returns a unique entry, raises exception if not unique
961989 """
963991 base,
964992 scope,
965993 filterstr,
966 attrlist=attrlist or ['*'],
994 attrlist=attrlist,
967995 attrsonly=attrsonly,
968996 serverctrls=serverctrls,
969997 clientctrls=clientctrls,
9741002 raise NO_UNIQUE_ENTRY('No or non-unique search result for %s' % (repr(filterstr)))
9751003 return r[0]
9761004
977 def read_rootdse_s(self, filterstr='(objectClass=*)', attrlist=None):
1005 def read_rootdse_s(self, filterstr=None, attrlist=None):
9781006 """
9791007 convenience wrapper around read_s() for reading rootDSE
9801008 """
1009 if self.bytes_mode:
1010 base = b''
1011 attrlist = attrlist or [b'*', b'+']
1012 else:
1013 base = u''
1014 attrlist = attrlist or [u'*', u'+']
9811015 ldap_rootdse = self.read_s(
982 '',
1016 base,
9831017 filterstr=filterstr,
984 attrlist=attrlist or ['*', '+'],
1018 attrlist=attrlist,
9851019 )
9861020 return ldap_rootdse # read_rootdse_s()
9871021
9901024 returns all attribute values of namingContexts in rootDSE
9911025 if namingContexts is not present (not readable) then empty list is returned
9921026 """
1027 if self.bytes_mode:
1028 name = b'namingContexts'
1029 else:
1030 name = u'namingContexts'
9931031 return self.read_rootdse_s(
994 attrlist=['namingContexts']
995 ).get('namingContexts', [])
1032 attrlist=[name]
1033 ).get(name, [])
9961034
9971035
9981036 class ReconnectLDAPObject(SimpleLDAPObject):
10681106 func(self,*args,**kwargs)
10691107 else:
10701108 # Send explicit anon simple bind request to provoke ldap.SERVER_DOWN in method reconnect()
1071 SimpleLDAPObject.simple_bind_s(self,'','')
1109 SimpleLDAPObject.simple_bind_s(self, None, None)
10721110
10731111 def _restore_options(self):
10741112 """Restore all recorded options"""
1010
1111 def addModlist(entry,ignore_attr_types=None):
1212 """Build modify list for call of method LDAPObject.add()"""
13 ignore_attr_types = set(map(str.lower,ignore_attr_types or []))
13 ignore_attr_types = {v.lower() for v in ignore_attr_types or []}
1414 modlist = []
1515 for attrtype in entry.keys():
1616 if attrtype.lower() in ignore_attr_types:
4545 List of attribute type names for which comparison will be made
4646 case-insensitive
4747 """
48 ignore_attr_types = set(map(str.lower,ignore_attr_types or []))
49 case_ignore_attr_types = set(map(str.lower,case_ignore_attr_types or []))
48 ignore_attr_types = {v.lower() for v in ignore_attr_types or []}
49 case_ignore_attr_types = {v.lower() for v in case_ignore_attr_types or []}
5050 modlist = []
5151 attrtype_lower_map = {}
5252 for a in old_entry.keys():
7272 replace_attr_value = len(old_value)!=len(new_value)
7373 if not replace_attr_value:
7474 if attrtype_lower in case_ignore_attr_types:
75 norm_func = str.lower
76 old_value_set = set(map(str.lower,old_value))
77 new_value_set = set(map(str.lower,new_value))
75 old_value_set = {v.lower() for v in old_value}
76 new_value_set = {v.lower() for v in new_value}
7877 else:
7978 old_value_set = set(old_value)
8079 new_value_set = set(new_value)
11 """
22 meta attributes for packaging which does not import any dependencies
33 """
4 __version__ = '3.0.0b3'
4 __version__ = '3.0.0b4'
55 __author__ = u'python-ldap project'
66 __license__ = 'Python style'
126126 This list of strings contains NAMEs or OIDs of object classes
127127 this object class is derived from
128128 """
129 schema_attribute = 'objectClasses'
129 schema_attribute = u'objectClasses'
130130 token_defaults = {
131131 'NAME':(()),
132132 'DESC':(None,),
224224 This list of strings contains NAMEs or OIDs of attribute types
225225 this attribute type is derived from
226226 """
227 schema_attribute = 'attributeTypes'
227 schema_attribute = u'attributeTypes'
228228 token_defaults = {
229229 'NAME':(()),
230230 'DESC':(None,),
318318 Integer flag (0 or 1) indicating whether the attribute type is marked
319319 as not human-readable (X-NOT-HUMAN-READABLE)
320320 """
321 schema_attribute = 'ldapSyntaxes'
321 schema_attribute = u'ldapSyntaxes'
322322 token_defaults = {
323323 'DESC':(None,),
324324 'X-NOT-HUMAN-READABLE':(None,),
366366 syntax
367367 String contains OID of the LDAP syntax this matching rule is usable with
368368 """
369 schema_attribute = 'matchingRules'
369 schema_attribute = u'matchingRules'
370370 token_defaults = {
371371 'NAME':(()),
372372 'DESC':(None,),
412412 This list of strings contains NAMEs or OIDs of attribute types
413413 for which this matching rule is used
414414 """
415 schema_attribute = 'matchingRuleUse'
415 schema_attribute = u'matchingRuleUse'
416416 token_defaults = {
417417 'NAME':(()),
418418 'DESC':(None,),
469469 This list of strings contains NAMEs or OIDs of attributes which
470470 may not be present in an entry of the object class
471471 """
472 schema_attribute = 'dITContentRules'
472 schema_attribute = u'dITContentRules'
473473 token_defaults = {
474474 'NAME':(()),
475475 'DESC':(None,),
526526 List of strings with NAMEs or OIDs of allowed structural object classes
527527 of superior entries in the DIT
528528 """
529 schema_attribute = 'dITStructureRules'
529 schema_attribute = u'dITStructureRules'
530530
531531 token_defaults = {
532532 'NAME':(()),
590590 This list of strings contains NAMEs or OIDs of additional attributes
591591 an RDN may contain
592592 """
593 schema_attribute = 'nameForms'
593 schema_attribute = u'nameForms'
594594 token_defaults = {
595595 'NAME':(()),
596596 'DESC':(None,),
33 See https://www.python-ldap.org/ for details.
44 """
55
6 __version__ = '3.0.0b3'
6 __version__ = '3.0.0b4'
77
88 __all__ = [
99 # constants
157157 ]
158158
159159 def __str__(self):
160 return ','.join(map(str,self.values()))
160 return ','.join(str(v) for v in self.values())
161161
162162 def __repr__(self):
163163 return '<%s.%s instance at %s: %s>' % (
55
66 from __future__ import unicode_literals
77
8 __version__ = '3.0.0b3'
8 __version__ = '3.0.0b4'
99
1010 __all__ = [
1111 # constants
576576
577577 class LDIFRecordList(LDIFParser):
578578 """
579 Collect all records of LDIF input into a single list.
580 of 2-tuples (dn,entry). It can be a memory hog!
579 Collect all records of a LDIF file. It can be a memory hog!
580
581 Records are stored in :attr:`.all_records` as a single list
582 of 2-tuples (dn, entry), after calling :meth:`.parse`.
581583 """
582584
583585 def __init__(
585587 input_file,
586588 ignored_attr_types=None,max_entries=0,process_url_schemes=None
587589 ):
588 """
589 See LDIFParser.__init__()
590
591 Additional Parameters:
592 all_records
593 List instance for storing parsed records
594 """
595590 LDIFParser.__init__(self,input_file,ignored_attr_types,max_entries,process_url_schemes)
591
592 #: List storing parsed records.
596593 self.all_records = []
597594 self.all_modify_changes = []
598595
599596 def handle(self,dn,entry):
600597 """
601 Append single record to dictionary of all records.
598 Append a single record to the list of all records (:attr:`.all_records`).
602599 """
603600 self.all_records.append((dn,entry))
604601
00 Metadata-Version: 1.2
11 Name: python-ldap
2 Version: 3.0.0b3
2 Version: 3.0.0b4
33 Summary: Python modules for implementing LDAP clients
44 Home-page: https://www.python-ldap.org/
55 Author: python-ldap project
2727 Classifier: Programming Language :: Python :: 2
2828 Classifier: Programming Language :: Python :: 2.7
2929 Classifier: Programming Language :: Python :: 3
30 Classifier: Programming Language :: Python :: 3.3
3130 Classifier: Programming Language :: Python :: 3.4
3231 Classifier: Programming Language :: Python :: 3.5
3332 Classifier: Programming Language :: Python :: 3.6
3635 Classifier: Topic :: Software Development :: Libraries :: Python Modules
3736 Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
3837 Classifier: License :: OSI Approved :: Python Software Foundation License
39 Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*
38 Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
44 See https://www.python-ldap.org/ for details.
55 """
66
7 __version__ = '3.0.0b3'
7 __version__ = '3.0.0b4'
88
99 from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler
1010 from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls
498498 LDAPControl** client_ldcs = NULL;
499499 struct berval cred;
500500
501 if (!PyArg_ParseTuple( args, "ss#|OO", &who, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL;
501 if (!PyArg_ParseTuple( args, "zz#|OO", &who, &cred.bv_val, &cred_len, &serverctrls, &clientctrls )) return NULL;
502502 cred.bv_len = (ber_len_t) cred_len;
503503
504504 if (not_valid(self)) return NULL;
00 Metadata-Version: 1.2
11 Name: python-ldap
2 Version: 3.0.0b3
2 Version: 3.0.0b4
33 Summary: Python modules for implementing LDAP clients
44 Home-page: https://www.python-ldap.org/
55 Author: python-ldap project
2727 Classifier: Programming Language :: Python :: 2
2828 Classifier: Programming Language :: Python :: 2.7
2929 Classifier: Programming Language :: Python :: 3
30 Classifier: Programming Language :: Python :: 3.3
3130 Classifier: Programming Language :: Python :: 3.4
3231 Classifier: Programming Language :: Python :: 3.5
3332 Classifier: Programming Language :: Python :: 3.6
3635 Classifier: Topic :: Software Development :: Libraries :: Python Modules
3736 Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP
3837 Classifier: License :: OSI Approved :: Python Software Foundation License
39 Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*
38 Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
66
77
88 import os
9 import shelve
10 import sys
911 import unittest
10 import shelve
12
13 if sys.version_info[0] <= 2:
14 PY2 = True
15 else:
16 PY2 = False
1117
1218 from slapdtest import SlapdObject, SlapdTestCase
1319
124130 Needs to be separate, because once an LDAP client starts a syncrepl
125131 search, it can't be used for anything else.
126132 """
127 server_class = SyncreplProvider
128
129 def __init__(self, uri, dn, password, storage=None):
133
134 def __init__(self, uri, dn, password, storage=None, filterstr=None,
135 **kwargs):
130136 """
131137 Set up our object by creating a search client, connecting, and binding.
132138 """
145151 self.data['cookie'] = None
146152 self.present = []
147153 self.refresh_done = False
148
149 SimpleLDAPObject.__init__(self, uri)
154 self.filterstr = filterstr
155
156 SimpleLDAPObject.__init__(self, uri, **kwargs)
150157 self.simple_bind_s(dn, password)
151
152158
153159 def unbind_s(self):
154160 """
160166 self.dn_attrs.close()
161167 SimpleLDAPObject.unbind_s(self)
162168
163
164169 def search(self, search_base, search_mode):
165170 """
166171 Start a syncrepl search operation, given a base DN and search mode.
169174 search_base,
170175 ldap.SCOPE_SUBTREE,
171176 mode=search_mode,
172 filterstr='(objectClass=*)'
173 )
174
177 filterstr=self.filterstr
178 )
175179
176180 def cancel(self):
177181 """
178182 A simple wrapper to call parent class with syncrepl search ID.
179183 """
180184 SimpleLDAPObject.cancel(self, self.search_id)
181
182185
183186 def poll(self, timeout=None, all=0):
184187 """
190193 all=all
191194 )
192195
193
194196 def syncrepl_get_cookie(self):
195197 """
196198 Pull cookie from storage, if one exists.
197199 """
198200 return self.data['cookie']
199201
200
201202 def syncrepl_set_cookie(self, cookie):
202203 """
203204 Update stored cookie.
204205 """
205206 self.data['cookie'] = cookie
206207
207
208208 def syncrepl_refreshdone(self):
209209 """
210210 Just update a variable.
211211 """
212212 self.refresh_done = True
213
214213
215214 def syncrepl_delete(self, uuids):
216215 """
219218 for uuid in uuids:
220219 del self.dn_attrs[self.uuid_dn[uuid]]
221220 del self.uuid_dn[uuid]
222
223221
224222 def syncrepl_entry(self, dn, attrs, uuid):
225223 """
235233 self.uuid_dn[uuid] = dn
236234 self.dn_attrs[dn] = attrs
237235
238
239236 def syncrepl_present(self, uuids, refreshDeletes=False):
240237 """
241238 The 'present' message from the LDAP server is the most complicated
261258 pass
262259
263260
264 class Test00_Syncrepl(SlapdTestCase):
261 class BaseSyncreplTests(object):
265262 """
266263 This is a test of all the basic Syncrepl operations. It covers starting a
267264 search (both types of search), doing the refresh part of the search,
274271
275272 @classmethod
276273 def setUpClass(cls):
277 super(Test00_Syncrepl, cls).setUpClass()
274 super(BaseSyncreplTests, cls).setUpClass()
278275 # insert some Foo* objects via ldapadd
279276 cls.server.ldapadd(
280277 LDIF_TEMPLATE % {
286283 }
287284 )
288285
289
290286 def setUp(self):
291 try:
292 self._ldap_conn
293 except AttributeError:
294 # open local LDAP connection
295 self._ldap_conn = self._open_ldap_conn()
296
287 super(BaseSyncreplTests, self).setUp()
288 self.tester = None
289 self.suffix = None
297290
298291 def tearDown(self):
299292 self.tester.unbind_s()
300
293 super(BaseSyncreplTests, self).tearDown()
294
295 def create_client(self):
296 raise NotImplementedError
301297
302298 def test_refreshOnly_search(self):
303299 '''
304300 Test to see if we can initialize a syncrepl search.
305301 '''
306 self.tester = SyncreplClient(
307 self.server.ldap_uri,
308 self.server.root_dn,
309 self.server.root_pw
310 )
311 self.tester.search(
312 self.server.suffix,
302 self.tester.search(
303 self.suffix,
313304 'refreshOnly'
314305 )
315306
316
317307 def test_refreshAndPersist_search(self):
318 self.tester = SyncreplClient(
319 self.server.ldap_uri,
320 self.server.root_dn,
321 self.server.root_pw
322 )
323 self.tester.search(
324 self.server.suffix,
308 self.tester.search(
309 self.suffix,
325310 'refreshAndPersist'
326311 )
327312
328
329313 def test_refreshOnly_poll_full(self):
330314 """
331315 Test doing a full refresh cycle, and check what we got.
332316 """
333 self.tester = SyncreplClient(
334 self.server.ldap_uri,
335 self.server.root_dn,
336 self.server.root_pw
337 )
338 self.tester.search(
339 self.server.suffix,
317 self.tester.search(
318 self.suffix,
340319 'refreshOnly'
341320 )
342321 poll_result = self.tester.poll(
346325 self.assertFalse(poll_result)
347326 self.assertEqual(self.tester.dn_attrs, LDAP_ENTRIES)
348327
349
350328 def test_refreshAndPersist_poll_only(self):
351329 """
352330 Test the refresh part of refresh-and-persist, and check what we got.
353331 """
354 self.tester = SyncreplClient(
355 self.server.ldap_uri,
356 self.server.root_dn,
357 self.server.root_pw
358 )
359 self.tester.search(
360 self.server.suffix,
332 self.tester.search(
333 self.suffix,
361334 'refreshAndPersist'
362335 )
363336
371344
372345 self.assertEqual(self.tester.dn_attrs, LDAP_ENTRIES)
373346
374
375347 def test_refreshAndPersist_timeout(self):
376348 """
377349 Make sure refreshAndPersist can handle a search with timeouts.
378350 """
379 self.tester = SyncreplClient(
380 self.server.ldap_uri,
381 self.server.root_dn,
382 self.server.root_pw
383 )
384 self.tester.search(
385 self.server.suffix,
351 self.tester.search(
352 self.suffix,
386353 'refreshAndPersist'
387354 )
388355
406373 timeout=1
407374 )
408375
409
410376 def test_refreshAndPersist_cancelled(self):
411377 """
412378 Make sure refreshAndPersist can handle cancelling a syncrepl search.
413379 """
414 self.tester = SyncreplClient(
415 self.server.ldap_uri,
416 self.server.root_dn,
417 self.server.root_pw
418 )
419 self.tester.search(
420 self.server.suffix,
380 self.tester.search(
381 self.suffix,
421382 'refreshAndPersist'
422383 )
423384
462423 # should pick it up during the persist phase.
463424
464425
426 class TestSyncrepl(BaseSyncreplTests, SlapdTestCase):
427 def setUp(self):
428 super(TestSyncrepl, self).setUp()
429 self.tester = SyncreplClient(
430 self.server.ldap_uri,
431 self.server.root_dn,
432 self.server.root_pw,
433 filterstr=u'(objectClass=*)',
434 bytes_mode=False
435 )
436 self.suffix = self.server.suffix
437
438
439 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
440 class TestSyncreplBytesMode(BaseSyncreplTests, SlapdTestCase):
441 def setUp(self):
442 super(TestSyncreplBytesMode, self).setUp()
443 self.tester = SyncreplClient(
444 self.server.ldap_uri,
445 self.server.root_dn.encode('utf-8'),
446 self.server.root_pw.encode('utf-8'),
447 filterstr=b'(objectClass=*)',
448 bytes_mode=True
449 )
450 self.suffix = self.server.suffix.encode('utf-8')
451
452
465453 if __name__ == '__main__':
466454 unittest.main()
161161 self.assertEqual(type(value), bytes)
162162
163163 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
164 def test_bytesmode_search_defaults(self):
165 l = self._get_bytes_ldapobject()
166 base = 'cn=Foo1,' + self.server.suffix
167 kwargs = dict(
168 base=base.encode('utf-8'),
169 scope=ldap.SCOPE_SUBTREE,
170 # filterstr=b'(objectClass=*)'
171 )
172 expected = [
173 (
174 base,
175 {'cn': [b'Foo1'], 'objectClass': [b'organizationalRole']}
176 ),
177 ]
178
179 result = l.search_s(**kwargs)
180 self.assertEqual(result, expected)
181 result = l.search_st(**kwargs)
182 self.assertEqual(result, expected)
183 result = l.search_ext_s(**kwargs)
184 self.assertEqual(result, expected)
185
186 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
164187 def test_unset_bytesmode_search_warns_bytes(self):
165188 l = self._get_bytes_ldapobject(explicit=False)
166189 base = self.server.suffix
262285 [('cn=Foo4,ou=Container,'+self.server.suffix, {'cn': [b'Foo4']})]
263286 )
264287
288 def test_find_unique_entry(self):
289 result = self._ldap_conn.find_unique_entry(
290 self.server.suffix,
291 ldap.SCOPE_SUBTREE,
292 '(cn=Foo4)',
293 ['cn'],
294 )
295 self.assertEqual(
296 result,
297 ('cn=Foo4,ou=Container,'+self.server.suffix, {'cn': [b'Foo4']})
298 )
299 with self.assertRaises(ldap.SIZELIMIT_EXCEEDED):
300 # > 2 entries returned
301 self._ldap_conn.find_unique_entry(
302 self.server.suffix,
303 ldap.SCOPE_ONELEVEL,
304 '(cn=Foo*)',
305 ['*'],
306 )
307 with self.assertRaises(ldap.NO_UNIQUE_ENTRY):
308 # 0 entries returned
309 self._ldap_conn.find_unique_entry(
310 self.server.suffix,
311 ldap.SCOPE_ONELEVEL,
312 '(cn=Bar*)',
313 ['*'],
314 )
315
265316 def test_search_subschema(self):
266317 l = self._ldap_conn
267318 dn = l.search_subschemasubentry_s()
268319 self.assertIsInstance(dn, text_type)
269320 self.assertEqual(dn, "cn=Subschema")
321 subschema = l.read_subschemasubentry_s(dn)
322 self.assertIsInstance(subschema, dict)
323 self.assertEqual(
324 sorted(subschema),
325 [
326 u'attributeTypes',
327 u'ldapSyntaxes',
328 u'matchingRuleUse',
329 u'matchingRules',
330 u'objectClasses'
331 ]
332 )
270333
271334 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
272335 def test_search_subschema_have_bytes(self):
273 l = self._get_bytes_ldapobject(explicit=False)
336 l = self._get_bytes_ldapobject()
274337 dn = l.search_subschemasubentry_s()
275338 self.assertIsInstance(dn, bytes)
276339 self.assertEqual(dn, b"cn=Subschema")
340 subschema = l.read_subschemasubentry_s(dn)
341 self.assertIsInstance(subschema, dict)
342 self.assertEqual(
343 sorted(subschema),
344 [
345 b'attributeTypes',
346 b'ldapSyntaxes',
347 b'matchingRuleUse',
348 b'matchingRules',
349 b'objectClasses'
350 ]
351 )
277352
278353 def test004_errno107(self):
279354 l = self.ldap_object_class('ldap://127.0.0.1:42')
324399 issubclass(cls, other),
325400 cls.__mro__
326401 )
402
403 def test_simple_bind_noarg(self):
404 l = self.ldap_object_class(self.server.ldap_uri)
405 l.simple_bind_s()
406 self.assertEqual(l.whoami_s(), u'')
407 l = self.ldap_object_class(self.server.ldap_uri)
408 l.simple_bind_s(None, None)
409 self.assertEqual(l.whoami_s(), u'')
327410
328411 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
329412 def test_ldapbyteswarning(self):
432515 l.simple_bind_s(self.server.root_dn, self.server.root_pw)
433516 self.assertEqual(l.whoami_s(), 'dn:' + self.server.root_dn)
434517
518 def test_dse(self):
519 dse = self._ldap_conn.read_rootdse_s()
520 self.assertIsInstance(dse, dict)
521 self.assertEqual(dse[u'supportedLDAPVersion'], [b'3'])
522 self.assertEqual(
523 sorted(dse),
524 [u'configContext', u'entryDN', u'namingContexts', u'objectClass',
525 u'structuralObjectClass', u'subschemaSubentry',
526 u'supportedControl', u'supportedExtension', u'supportedFeatures',
527 u'supportedLDAPVersion', u'supportedSASLMechanisms']
528 )
529 self.assertEqual(
530 self._ldap_conn.get_naming_contexts(),
531 [self.server.suffix.encode('utf-8')]
532 )
533
534 @unittest.skipUnless(PY2, "no bytes_mode under Py3")
535 def test_dse_bytes(self):
536 l = self._get_bytes_ldapobject()
537 dse = l.read_rootdse_s()
538 self.assertIsInstance(dse, dict)
539 self.assertEqual(dse[u'supportedLDAPVersion'], [b'3'])
540 self.assertEqual(
541 l.get_naming_contexts(),
542 [self.server.suffix.encode('utf-8')]
543 )
544
435545
436546 class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject):
437547 """
88
99 if sys.version_info[0] == 2 and sys.version_info[1] < 7:
1010 raise RuntimeError('This software requires Python 2.7 or 3.x.')
11 if sys.version_info[0] >= 3 and sys.version_info < (3, 3):
12 raise RuntimeError('The C API from Python 3.3+ is required.')
11 if sys.version_info[0] >= 3 and sys.version_info < (3, 4):
12 raise RuntimeError('The C API from Python 3.4+ is required.')
1313
1414 if sys.version_info[0] >= 3:
1515 from configparser import ConfigParser
9090 'Programming Language :: Python :: 2',
9191 'Programming Language :: Python :: 2.7',
9292 'Programming Language :: Python :: 3',
93 'Programming Language :: Python :: 3.3',
9493 'Programming Language :: Python :: 3.4',
9594 'Programming Language :: Python :: 3.5',
9695 'Programming Language :: Python :: 3.6',
167166 'pyasn1_modules >= 0.1.5',
168167 ],
169168 zip_safe=False,
170 python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*',
169 python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
171170 test_suite = 'Tests',
172171 )
44
55 [tox]
66 # Note: when updating Python versions, also change setup.py and .travis.yml
7 envlist = py27,py33,py34,py35,py36,{py2,py3}-nosasltls,doc,coverage-report
7 envlist = py27,py34,py35,py36,{py2,py3}-nosasltls,doc,coverage-report
88 minver = 1.8
99
1010 [testenv]
1212 passenv = WITH_GCOV
1313 # - Enable BytesWarning
1414 # - Turn all warnings into exceptions.
15 # - 'ignore:the imp module is deprecated' is required to ignore import of
16 # 'imp' in distutils. Python 3.3 and 3.4 use PendingDeprecationWarning.
15 # - 'ignore:the imp module is deprecated' is required to ignore import of 'imp'
16 # in distutils. Python < 3.6 use PendingDeprecationWarning; Python >= 3.6 use
17 # DeprecationWarning.
1718 commands = {envpython} -bb -Werror \
1819 "-Wignore:the imp module is deprecated:DeprecationWarning" \
1920 "-Wignore:the imp module is deprecated:PendingDeprecationWarning" \