Development of an internal social media platform with personalised dashboards for students
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.

mockAsync.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. """
  2. """
  3. # Created on 2016.04.30
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2016 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. from .. import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, NO_ATTRIBUTES
  25. from .mockBase import MockBaseStrategy
  26. from .asynchronous import AsyncStrategy
  27. from ..operation.search import search_result_done_response_to_dict, search_result_entry_response_to_dict
  28. from ..core.results import DO_NOT_RAISE_EXCEPTIONS
  29. from ..utils.log import log, log_enabled, ERROR, PROTOCOL
  30. from ..core.exceptions import LDAPResponseTimeoutError, LDAPOperationResult
  31. from ..operation.bind import bind_response_to_dict
  32. from ..operation.delete import delete_response_to_dict
  33. from ..operation.add import add_response_to_dict
  34. from ..operation.compare import compare_response_to_dict
  35. from ..operation.modifyDn import modify_dn_response_to_dict
  36. from ..operation.modify import modify_response_to_dict
  37. from ..operation.search import search_result_done_response_to_dict, search_result_entry_response_to_dict
  38. from ..operation.extended import extended_response_to_dict
  39. # LDAPResult ::= SEQUENCE {
  40. # resultCode ENUMERATED {
  41. # success (0),
  42. # operationsError (1),
  43. # protocolError (2),
  44. # timeLimitExceeded (3),
  45. # sizeLimitExceeded (4),
  46. # compareFalse (5),
  47. # compareTrue (6),
  48. # authMethodNotSupported (7),
  49. # strongerAuthRequired (8),
  50. # -- 9 reserved --
  51. # referral (10),
  52. # adminLimitExceeded (11),
  53. # unavailableCriticalExtension (12),
  54. # confidentialityRequired (13),
  55. # saslBindInProgress (14),
  56. # noSuchAttribute (16),
  57. # undefinedAttributeType (17),
  58. # inappropriateMatching (18),
  59. # constraintViolation (19),
  60. # attributeOrValueExists (20),
  61. # invalidAttributeSyntax (21),
  62. # -- 22-31 unused --
  63. # noSuchObject (32),
  64. # aliasProblem (33),
  65. # invalidDNSyntax (34),
  66. # -- 35 reserved for undefined isLeaf --
  67. # aliasDereferencingProblem (36),
  68. # -- 37-47 unused --
  69. # inappropriateAuthentication (48),
  70. # invalidCredentials (49),
  71. # insufficientAccessRights (50),
  72. # busy (51),
  73. # unavailable (52),
  74. # unwillingToPerform (53),
  75. # loopDetect (54),
  76. # -- 55-63 unused --
  77. # namingViolation (64),
  78. # objectClassViolation (65),
  79. # notAllowedOnNonLeaf (66),
  80. # notAllowedOnRDN (67),
  81. # entryAlreadyExists (68),
  82. # objectClassModsProhibited (69),
  83. # -- 70 reserved for CLDAP --
  84. # affectsMultipleDSAs (71),
  85. # -- 72-79 unused --
  86. # other (80),
  87. # ... },
  88. # matchedDN LDAPDN,
  89. # diagnosticMessage LDAPString,
  90. # referral [3] Referral OPTIONAL }
  91. class MockAsyncStrategy(MockBaseStrategy, AsyncStrategy): # class inheritance sequence is important, MockBaseStrategy must be the first one
  92. """
  93. This strategy create a mock LDAP server, with asynchronous access
  94. It can be useful to test LDAP without accessing a real Server
  95. """
  96. def __init__(self, ldap_connection):
  97. AsyncStrategy.__init__(self, ldap_connection)
  98. MockBaseStrategy.__init__(self)
  99. #outstanding = dict() # a dictionary with the message id as key and a tuple (result, response) as value
  100. def post_send_search(self, payload):
  101. message_id, message_type, request, controls = payload
  102. async_response = []
  103. async_result = dict()
  104. if message_type == 'searchRequest':
  105. responses, result = self.mock_search(request, controls)
  106. result['type'] = 'searchResDone'
  107. for entry in responses:
  108. response = search_result_entry_response_to_dict(entry, self.connection.server.schema, self.connection.server.custom_formatter, self.connection.check_names)
  109. response['type'] = 'searchResEntry'
  110. if self.connection.empty_attributes:
  111. for attribute_type in request['attributes']:
  112. attribute_name = str(attribute_type)
  113. if attribute_name not in response['raw_attributes'] and attribute_name not in (ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, NO_ATTRIBUTES):
  114. response['raw_attributes'][attribute_name] = list()
  115. response['attributes'][attribute_name] = list()
  116. if log_enabled(PROTOCOL):
  117. log(PROTOCOL, 'attribute set to empty list for missing attribute <%s> in <%s>',
  118. attribute_type, self)
  119. if not self.connection.auto_range:
  120. attrs_to_remove = []
  121. # removes original empty attribute in case a range tag is returned
  122. for attribute_type in response['attributes']:
  123. attribute_name = str(attribute_type)
  124. if ';range' in attribute_name.lower():
  125. orig_attr, _, _ = attribute_name.partition(';')
  126. attrs_to_remove.append(orig_attr)
  127. for attribute_type in attrs_to_remove:
  128. if log_enabled(PROTOCOL):
  129. log(PROTOCOL,
  130. 'attribute type <%s> removed in response because of same attribute returned as range by the server in <%s>',
  131. attribute_type, self)
  132. del response['raw_attributes'][attribute_type]
  133. del response['attributes'][attribute_type]
  134. async_response.append(response)
  135. async_result = search_result_done_response_to_dict(result)
  136. async_result['type'] = 'searchResDone'
  137. self._responses[message_id] = (request, async_result, async_response)
  138. return message_id
  139. def post_send_single_response(self, payload): # payload is a tuple sent by self.send() made of message_type, request, controls
  140. message_id, message_type, request, controls = payload
  141. responses = []
  142. result = None
  143. if message_type == 'bindRequest':
  144. result = bind_response_to_dict(self.mock_bind(request, controls))
  145. result['type'] = 'bindResponse'
  146. elif message_type == 'unbindRequest':
  147. self.bound = None
  148. elif message_type == 'abandonRequest':
  149. pass
  150. elif message_type == 'delRequest':
  151. result = delete_response_to_dict(self.mock_delete(request, controls))
  152. result['type'] = 'delResponse'
  153. elif message_type == 'addRequest':
  154. result = add_response_to_dict(self.mock_add(request, controls))
  155. result['type'] = 'addResponse'
  156. elif message_type == 'compareRequest':
  157. result = compare_response_to_dict(self.mock_compare(request, controls))
  158. result['type'] = 'compareResponse'
  159. elif message_type == 'modDNRequest':
  160. result = modify_dn_response_to_dict(self.mock_modify_dn(request, controls))
  161. result['type'] = 'modDNResponse'
  162. elif message_type == 'modifyRequest':
  163. result = modify_response_to_dict(self.mock_modify(request, controls))
  164. result['type'] = 'modifyResponse'
  165. elif message_type == 'extendedReq':
  166. result = extended_response_to_dict(self.mock_extended(request, controls))
  167. result['type'] = 'extendedResp'
  168. responses.append(result)
  169. if self.connection.raise_exceptions and result and result['result'] not in DO_NOT_RAISE_EXCEPTIONS:
  170. if log_enabled(PROTOCOL):
  171. log(PROTOCOL, 'operation result <%s> for <%s>', result, self.connection)
  172. raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type'])
  173. self._responses[message_id] = (request, result, responses)
  174. return message_id
  175. def get_response(self, message_id, timeout=None, get_request=False):
  176. if message_id in self._responses:
  177. request, result, response = self._responses.pop(message_id)
  178. else:
  179. raise(LDAPResponseTimeoutError('message id not in outstanding queue'))
  180. if self.connection.raise_exceptions and result and result['result'] not in DO_NOT_RAISE_EXCEPTIONS:
  181. if log_enabled(PROTOCOL):
  182. log(PROTOCOL, 'operation result <%s> for <%s>', result, self.connection)
  183. raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type'])
  184. if get_request:
  185. return response, result, request
  186. else:
  187. return response, result