def test_autodiscover_path_1_2_3_no301_4(self, m): # Test steps 1 -> 2 -> 3 -> no 301 response -> 4 d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) m.post(self.dummy_ad_endpoint, status_code=501) m.post(f"https://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=501) m.get(f"http://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=200) with self.assertRaises(AutoDiscoverFailed): # Fails in step 4 with invalid SRV entry ad_response, _ = d.discover()
def test_autodiscover_path_1_5_valid_redirect_url_invalid_response(self, m): # Test steps 1 -> -> 5 -> Invalid response from redirect URL clear_cache() d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) redirect_url = "https://httpbin.org/Autodiscover/Autodiscover.xml" m.post(self.dummy_ad_endpoint, status_code=200, content=self.redirect_url_xml(redirect_url)) m.head(redirect_url, status_code=501) m.post(redirect_url, status_code=501) with self.assertRaises(AutoDiscoverFailed): # Fails in step 5 with invalid response ad_response, _ = d.discover()
def test_autodiscover_path_1_5_invalid_redirect_url(self, m): # Test steps 1 -> -> 5 -> Invalid redirect URL clear_cache() d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) m.post( self.dummy_ad_endpoint, status_code=200, content=self.redirect_url_xml(f"https://{get_random_hostname()}/EWS/Exchange.asmx"), ) with self.assertRaises(AutoDiscoverFailed): # Fails in step 5 with invalid redirect URL ad_response, _ = d.discover()
def test_autodiscover_cache(self, m): # Mock the default endpoint that we test in step 1 of autodiscovery m.post(self.dummy_ad_endpoint, status_code=200, content=self.dummy_ad_response) discovery = Autodiscovery( email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials, ) # Not cached self.assertNotIn(discovery._cache_key, autodiscover_cache) discovery.discover() # Now it's cached self.assertIn(discovery._cache_key, autodiscover_cache) # Make sure the cache can be looked by value, not by id(). This is important for multi-threading/processing self.assertIn( ( self.account.primary_smtp_address.split("@")[1], Credentials(self.account.protocol.credentials.username, self.account.protocol.credentials.password), True, ), autodiscover_cache, ) # Poison the cache with a failing autodiscover endpoint. discover() must handle this and rebuild the cache p = self.get_test_protocol() autodiscover_cache[discovery._cache_key] = p m.post("https://example.com/Autodiscover/Autodiscover.xml", status_code=404) discovery.discover() self.assertIn(discovery._cache_key, autodiscover_cache) # Make sure that the cache is actually used on the second call to discover() _orig = discovery._step_1 def _mock(slf, *args, **kwargs): raise NotImplementedError() discovery._step_1 = MethodType(_mock, discovery) discovery.discover() # Fake that another thread added the cache entry into the persistent storage but we don't have it in our # in-memory cache. The cache should work anyway. autodiscover_cache._protocols.clear() discovery.discover() discovery._step_1 = _orig # Make sure we can delete cache entries even though we don't have it in our in-memory cache autodiscover_cache._protocols.clear() del autodiscover_cache[discovery._cache_key] # This should also work if the cache does not contain the entry anymore del autodiscover_cache[discovery._cache_key]
def test_autodiscover_path_1_5_valid_redirect_url_valid_response(self, m): # Test steps 1 -> -> 5 -> Valid response from redirect URL -> 5 clear_cache() d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) redirect_hostname = "httpbin.org" redirect_url = f"https://{redirect_hostname}/Autodiscover/Autodiscover.xml" ews_url = f"https://{redirect_hostname}/EWS/Exchange.asmx" email = f"john@redirected.{redirect_hostname}" m.post(self.dummy_ad_endpoint, status_code=200, content=self.redirect_url_xml(redirect_url)) m.head(redirect_url, status_code=200) m.post(redirect_url, status_code=200, content=self.settings_xml(email, ews_url)) ad_response, _ = d.discover() self.assertEqual(ad_response.autodiscover_smtp_address, email) self.assertEqual(ad_response.protocol.ews_url, ews_url)
def test_autodiscover_path_1_2_3_4_invalid_srv(self, m): # Test steps 1 -> 2 -> 3 -> 4 -> invalid SRV URL d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) m.post(self.dummy_ad_endpoint, status_code=501) m.post(f"https://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=501) m.get(f"http://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=200) tmp = d._get_srv_records d._get_srv_records = Mock(return_value=[SrvRecord(1, 1, 443, get_random_hostname())]) try: with self.assertRaises(AutoDiscoverFailed): # Fails in step 4 with invalid response ad_response, _ = d.discover() finally: d._get_srv_records = tmp
def test_autodiscover_path_1_2_3_invalid301_4(self, m): # Test steps 1 -> 2 -> 3 -> invalid 301 URL -> 4 clear_cache() d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) m.post(self.dummy_ad_endpoint, status_code=501) m.post(f"https://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=501) m.get( f"http://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=301, headers=dict(location="XXX"), ) with self.assertRaises(AutoDiscoverFailed): # Fails in step 4 with invalid SRV entry ad_response, _ = d.discover()
def test_autodiscover_path_1_2_5(self, m): # Test steps 1 -> 2 -> 5 clear_cache() d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) ews_url = f"https://xxx.{self.domain}/EWS/Exchange.asmx" email = f"xxxd@{self.domain}" m.post(self.dummy_ad_endpoint, status_code=501) m.post( f"https://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=200, content=self.settings_xml(email, ews_url), ) ad_response, _ = d.discover() self.assertEqual(ad_response.autodiscover_smtp_address, email) self.assertEqual(ad_response.protocol.ews_url, ews_url)
def test_autodiscover_path_1_2_3_4_valid_srv_valid_response(self, m): # Test steps 1 -> 2 -> 3 -> 4 -> 5 d = Autodiscovery(email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials) redirect_srv = "httpbin.org" ews_url = f"https://{redirect_srv}/EWS/Exchange.asmx" redirect_email = f"john@redirected.{redirect_srv}" m.post(self.dummy_ad_endpoint, status_code=501) m.post(f"https://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=501) m.get(f"http://autodiscover.{self.domain}/Autodiscover/Autodiscover.xml", status_code=200) m.head(f"https://{redirect_srv}/Autodiscover/Autodiscover.xml", status_code=200) m.post( f"https://{redirect_srv}/Autodiscover/Autodiscover.xml", status_code=200, content=self.settings_xml(redirect_email, ews_url), ) tmp = d._get_srv_records d._get_srv_records = Mock(return_value=[SrvRecord(1, 1, 443, redirect_srv)]) try: ad_response, _ = d.discover() self.assertEqual(ad_response.autodiscover_smtp_address, redirect_email) self.assertEqual(ad_response.protocol.ews_url, ews_url) finally: d._get_srv_records = tmp
def test_autodiscover_redirect(self, m): # Test various aspects of autodiscover redirection. Mock all HTTP responses because we can't force a live server # to send us into the correct code paths. # Mock the default endpoint that we test in step 1 of autodiscovery m.post(self.dummy_ad_endpoint, status_code=200, content=self.dummy_ad_response) discovery = Autodiscovery( email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials, ) discovery.discover() # Make sure we discover a different return address m.post( self.dummy_ad_endpoint, status_code=200, content=self.settings_xml("*****@*****.**", "https://expr.example.com/EWS/Exchange.asmx"), ) ad_response, _ = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, "*****@*****.**") # Make sure we discover an address redirect to the same domain. We have to mock the same URL with two different # responses. We do that with a response list. m.post( self.dummy_ad_endpoint, [ dict(status_code=200, content=self.redirect_address_xml(f"redirect_me@{self.domain}")), dict( status_code=200, content=self.settings_xml( f"redirected@{self.domain}", f"https://redirected.{self.domain}/EWS/Exchange.asmx" ), ), ], ) ad_response, _ = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, f"redirected@{self.domain}") self.assertEqual(ad_response.protocol.ews_url, f"https://redirected.{self.domain}/EWS/Exchange.asmx") # Test that we catch circular redirects on the same domain with a primed cache. Just mock the endpoint to # return the same redirect response on every request. self.assertEqual(len(autodiscover_cache), 1) m.post(self.dummy_ad_endpoint, status_code=200, content=self.redirect_address_xml(f"foo@{self.domain}")) self.assertEqual(len(autodiscover_cache), 1) with self.assertRaises(AutoDiscoverCircularRedirect): discovery.discover() # Test that we also catch circular redirects when cache is empty clear_cache() self.assertEqual(len(autodiscover_cache), 0) with self.assertRaises(AutoDiscoverCircularRedirect): discovery.discover() # Test that we can handle being asked to redirect to an address on a different domain # Don't use example.com to redirect - it does not resolve or answer on all ISPs ews_hostname = "httpbin.org" redirect_email = f"john@redirected.{ews_hostname}" ews_url = f"https://{ews_hostname}/EWS/Exchange.asmx" m.post(self.dummy_ad_endpoint, status_code=200, content=self.redirect_address_xml(f"john@{ews_hostname}")) m.post( f"https://{ews_hostname}/Autodiscover/Autodiscover.xml", status_code=200, content=self.settings_xml(redirect_email, ews_url), ) ad_response, _ = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, redirect_email) self.assertEqual(ad_response.protocol.ews_url, ews_url) # Test redirect via HTTP 301 clear_cache() redirect_url = f"https://{ews_hostname}/OtherPath/Autodiscover.xml" redirect_email = f"john@otherpath.{ews_hostname}" ews_url = f"https://xxx.{ews_hostname}/EWS/Exchange.asmx" discovery.email = self.account.primary_smtp_address m.post(self.dummy_ad_endpoint, status_code=301, headers=dict(location=redirect_url)) m.post(redirect_url, status_code=200, content=self.settings_xml(redirect_email, ews_url)) m.head(redirect_url, status_code=200) ad_response, _ = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, redirect_email) self.assertEqual(ad_response.protocol.ews_url, ews_url)