|
23 | 23 | from test.support import threading_helper |
24 | 24 | from test.support import asyncore |
25 | 25 | from test.support import smtpd |
26 | | -from unittest.mock import Mock |
| 26 | +from unittest.mock import Mock, patch |
27 | 27 |
|
28 | 28 |
|
29 | 29 | support.requires_working_socket(module=True) |
@@ -1570,5 +1570,60 @@ def testAUTH_PLAIN_initial_response_auth(self): |
1570 | 1570 | self.assertEqual(code, 235) |
1571 | 1571 |
|
1572 | 1572 |
|
| 1573 | +class TestSMTPLoginValueError(unittest.TestCase): |
| 1574 | + def broken_hmac(*args, **kwargs): |
| 1575 | + raise ValueError("[digital envelope routines] unsupported") |
| 1576 | + |
| 1577 | + def test_login_raises_valueerror_when_cram_md5_fails(self): |
| 1578 | + with patch("hmac.HMAC", self.broken_hmac): |
| 1579 | + class FakeSMTP(smtplib.SMTP): |
| 1580 | + def __init__(self): |
| 1581 | + super().__init__(host='', port=0) |
| 1582 | + self.esmtp_features = {"auth": "CRAM-MD5"} |
| 1583 | + self._host = "localhost" |
| 1584 | + |
| 1585 | + def ehlo_or_helo_if_needed(self): |
| 1586 | + pass |
| 1587 | + |
| 1588 | + def has_extn(self, ext): |
| 1589 | + return ext.lower() == "auth" |
| 1590 | + |
| 1591 | + def docmd(self, *args, **kwargs): |
| 1592 | + # Retorna uma challenge base64 válida |
| 1593 | + return 334, b"Y2hhbGxlbmdl" |
| 1594 | + |
| 1595 | + smtp = FakeSMTP() |
| 1596 | + with self.assertRaises(ValueError) as ctx: |
| 1597 | + smtp.login("user", "pass") |
| 1598 | + self.assertIn("unsupported", str(ctx.exception).lower()) |
| 1599 | + |
| 1600 | + def test_login_fallbacks_when_cram_md5_raises_valueerror(self): |
| 1601 | + with patch("hmac.HMAC", self.broken_hmac): |
| 1602 | + class FakeSMTP(smtplib.SMTP): |
| 1603 | + def __init__(self): |
| 1604 | + super().__init__(host='', port=0) |
| 1605 | + self.esmtp_features = {"auth": "CRAM-MD5 LOGIN"} |
| 1606 | + self._host = "localhost" |
| 1607 | + |
| 1608 | + def ehlo_or_helo_if_needed(self): |
| 1609 | + pass |
| 1610 | + |
| 1611 | + def has_extn(self, ext): |
| 1612 | + return ext.lower() == "auth" |
| 1613 | + |
| 1614 | + def docmd(self, *args, **kwargs): |
| 1615 | + if args[0] == "AUTH" and args[1].startswith("CRAM-MD5"): |
| 1616 | + return 334, b"Y2hhbGxlbmdl" # base64('challenge') |
| 1617 | + return 235, b"Authentication successful" |
| 1618 | + |
| 1619 | + def auth_login(self, challenge=None): |
| 1620 | + return "login response" |
| 1621 | + |
| 1622 | + smtp = FakeSMTP() |
| 1623 | + code, resp = smtp.login("user", "pass") |
| 1624 | + self.assertEqual(code, 235) |
| 1625 | + self.assertEqual(resp, b"Authentication successful") |
| 1626 | + |
| 1627 | + |
1573 | 1628 | if __name__ == '__main__': |
1574 | 1629 | unittest.main() |
0 commit comments