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) # Also mock the EWS URL. We try to guess its auth method as part of autodiscovery m.post(self.dummy_ews_endpoint, status_code=200) discovery = Autodiscovery( email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials, retry_policy=self.retry_policy, ) # 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 autodiscover_cache[discovery._cache_key] = AutodiscoverProtocol( config=Configuration( service_endpoint= 'https://example.com/Autodiscover/Autodiscover.xml', credentials=Credentials(get_random_string(8), get_random_string(8)), auth_type=NTLM, retry_policy=FailFast(), )) 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_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) # Also mock the EWS URL. We try to guess its auth method as part of autodiscovery m.post(self.dummy_ews_endpoint, status_code=200) discovery = Autodiscovery( email=self.account.primary_smtp_address, credentials=self.account.protocol.credentials, retry_policy=self.retry_policy, ) discovery.discover() # Make sure we discover a different return address m.post(self.dummy_ad_endpoint, status_code=200, content=b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <User> <AutoDiscoverSMTPAddress>[email protected]</AutoDiscoverSMTPAddress> </User> <Account> <AccountType>email</AccountType> <Action>settings</Action> <Protocol> <Type>EXPR</Type> <EwsUrl>https://expr.example.com/EWS/Exchange.asmx</EwsUrl> </Protocol> </Account> </Response> </Autodiscover>''') # Also mock the EWS URL. We try to guess its auth method as part of autodiscovery m.post('https://expr.example.com/EWS/Exchange.asmx', status_code=200) ad_response, p = 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. redirect_addr_content = b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Account> <Action>redirectAddr</Action> <RedirectAddr>redirect_me@%s</RedirectAddr> </Account> </Response> </Autodiscover>''' % self.domain.encode() settings_content = b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <User> <AutoDiscoverSMTPAddress>redirected@%s</AutoDiscoverSMTPAddress> </User> <Account> <AccountType>email</AccountType> <Action>settings</Action> <Protocol> <Type>EXPR</Type> <EwsUrl>https://redirected.%s/EWS/Exchange.asmx</EwsUrl> </Protocol> </Account> </Response> </Autodiscover>''' % (self.domain.encode(), self.domain.encode()) # Also mock the EWS URL. We try to guess its auth method as part of autodiscovery m.post('https://redirected.%s/EWS/Exchange.asmx' % self.domain, status_code=200) m.post(self.dummy_ad_endpoint, [ dict(status_code=200, content=redirect_addr_content), dict(status_code=200, content=settings_content), ]) ad_response, p = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, 'redirected@%s' % self.domain) self.assertEqual( ad_response.ews_url, 'https://redirected.%s/EWS/Exchange.asmx' % self.domain) # 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=b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Account> <Action>redirectAddr</Action> <RedirectAddr>foo@%s</RedirectAddr> </Account> </Response> </Autodiscover>''' % self.domain.encode()) 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 m.post(self.dummy_ad_endpoint, status_code=200, content=b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <Account> <Action>redirectAddr</Action> <RedirectAddr>[email protected]</RedirectAddr> </Account> </Response> </Autodiscover>''') m.post('https://example.com/Autodiscover/Autodiscover.xml', status_code=200, content=b'''\ <?xml version="1.0" encoding="utf-8"?> <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006"> <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"> <User> <AutoDiscoverSMTPAddress>[email protected]</AutoDiscoverSMTPAddress> </User> <Account> <AccountType>email</AccountType> <Action>settings</Action> <Protocol> <Type>EXPR</Type> <EwsUrl>https://redirected.example.com/EWS/Exchange.asmx</EwsUrl> </Protocol> </Account> </Response> </Autodiscover>''') # Also mock the EWS URL. We try to guess its auth method as part of autodiscovery m.post('https://redirected.example.com/EWS/Exchange.asmx', status_code=200) ad_response, p = discovery.discover() self.assertEqual(ad_response.autodiscover_smtp_address, '*****@*****.**') self.assertEqual(ad_response.ews_url, 'https://redirected.example.com/EWS/Exchange.asmx')