def test_EmailDistributor_getBridges_rate_limit_expiry(self): """A client's first email should return bridges. The second should return a warning, and the third should receive no response. After the EmailDistributor.emailRateMax is up, the client should be able to receive a response again. """ clock = Clock() dist = EmailDistributor(self.key, self.domainmap, self.domainrules) [dist.hashring.insert(bridge) for bridge in self.bridges] bridgeRequest = self.makeClientRequest('*****@*****.**') # The first request should get a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest, 1, clock)), 3) # The second gets a warning, and the rest are ignored. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest, 1, clock) self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1, clock) self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1, clock) self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1, clock) clock.advance(2 * dist.emailRateMax) # The client should again a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest, 1)), 3, clock)
def test_EmailDistributor_getBridges_rate_limit_multiple_clients(self): """Each client should be rate-limited separately.""" dist = EmailDistributor(self.key, self.domainmap, self.domainrules) [dist.hashring.insert(bridge) for bridge in self.bridges] bridgeRequest1 = self.makeClientRequest('*****@*****.**') bridgeRequest2 = self.makeClientRequest('*****@*****.**') bridgeRequest3 = self.makeClientRequest('*****@*****.**') # The first request from 'abc' should get a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest1, 1)), 3) # The second from 'abc' gets a warning. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest1, 1) # The first request from 'def' should get a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest2, 1)), 3) # The third from 'abc' is ignored. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest1, 1) # The second from 'def' gets a warning. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest2, 1) # The third from 'def' is ignored. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest2, 1) # The fourth from 'abc' is ignored. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest1, 1) # The first request from 'ghi' should get a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest3, 1)), 3) # The second from 'ghi' gets a warning. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest3, 1) # The third from 'ghi' is ignored. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest3, 1) # The fourth from 'ghi' is ignored. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest3, 1)
def test_EmailDistributor_getBridges_rate_limit(self): """A client's first email should return bridges. The second should return a warning, and the third should receive no response. """ dist = EmailDistributor(self.key, self.domainmap, self.domainrules) [dist.hashring.insert(bridge) for bridge in self.bridges] bridgeRequest = self.makeClientRequest('*****@*****.**') # The first request should get a response with bridges. bridges = dist.getBridges(bridgeRequest, 1) self.assertGreater(len(bridges), 0) [self.assertIsInstance(b, Bridge) for b in bridges] self.assertEqual(len(bridges), 3) # The second gets a warning, and the third is ignored. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest, 1) self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1)
def test_EmailDistributor_getBridges_with_whitelist(self): """If an email address is in the whitelist, it should get a response every time it asks (i.e. no rate-limiting). """ # The whitelist should be in the form {EMAIL: GPG_FINGERPRINT} whitelist = { '*****@*****.**': '0123456789ABCDEF0123456789ABCDEF01234567' } dist = EmailDistributor(self.key, self.domainmap, self.domainrules, whitelist=whitelist) [dist.hashring.insert(bridge) for bridge in self.bridges] # A request from a whitelisted address should always get a response. bridgeRequest = self.makeClientRequest('*****@*****.**') for i in range(5): bridges = dist.getBridges(bridgeRequest, 1) self.assertEqual(len(bridges), 3)
def test_EmailDistributor_cleanDatabase(self): """Calling cleanDatabase() should cleanup email times in database, but not allow clients who have been recently warned and/or ignored to receive a response again until the remainder of their MAX_EMAIL_RATE time is up. """ dist = EmailDistributor(self.key, self.domainmap, self.domainrules) [dist.hashring.insert(bridge) for bridge in self.bridges] bridgeRequest = self.makeClientRequest('*****@*****.**') # The first request should get a response with bridges. self.assertEqual(len(dist.getBridges(bridgeRequest, 1)), 3) # The second gets a warning, and the third is ignored. self.assertRaises(TooSoonEmail, dist.getBridges, bridgeRequest, 1) self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1) dist.cleanDatabase() # Cleaning the warning email times in the database shouldn't cause # '*****@*****.**' to be able to email again, because only the times # which aren't older than EMAIL_MAX_RATE should be cleared. self.assertRaises(IgnoreEmail, dist.getBridges, bridgeRequest, 1)