From 0a34db8a107c430a7bab266feaff75f0e8d99ed9 Mon Sep 17 00:00:00 2001 From: Pierre Verkest Date: Tue, 8 Oct 2024 16:12:23 +0200 Subject: [PATCH 1/2] [FIX] google_gmail: add 5s thershold to avoid using expired token fix this issue: https://github.com/odoo/odoo/issues/182777 --- .../google_gmail/models/google_gmail_mixin.py | 6 ++- addons/google_gmail/tests/__init__.py | 1 + .../google_gmail/tests/test_google_gmail.py | 50 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 addons/google_gmail/tests/__init__.py create mode 100644 addons/google_gmail/tests/test_google_gmail.py diff --git a/addons/google_gmail/models/google_gmail_mixin.py b/addons/google_gmail/models/google_gmail_mixin.py index 289a0443fb81f..28ecef919764b 100644 --- a/addons/google_gmail/models/google_gmail_mixin.py +++ b/addons/google_gmail/models/google_gmail_mixin.py @@ -144,10 +144,14 @@ def _generate_oauth2_string(self, user, refresh_token): :return: The SASL argument for the OAuth2 mechanism. """ self.ensure_one() + # token life is around 1 hour, add 5s threshold to gives + # time to contact google servers and avoid clock synchronization + # problems + threshold_seconds = 5 now_timestamp = int(time.time()) if not self.google_gmail_access_token \ or not self.google_gmail_access_token_expiration \ - or self.google_gmail_access_token_expiration < now_timestamp: + or self.google_gmail_access_token_expiration - threshold_seconds < now_timestamp: access_token, expiration = self._fetch_gmail_access_token(self.google_gmail_refresh_token) diff --git a/addons/google_gmail/tests/__init__.py b/addons/google_gmail/tests/__init__.py new file mode 100644 index 0000000000000..7a800575a7fe4 --- /dev/null +++ b/addons/google_gmail/tests/__init__.py @@ -0,0 +1 @@ +from . import test_google_gmail diff --git a/addons/google_gmail/tests/test_google_gmail.py b/addons/google_gmail/tests/test_google_gmail.py new file mode 100644 index 0000000000000..9b8baf00abc2c --- /dev/null +++ b/addons/google_gmail/tests/test_google_gmail.py @@ -0,0 +1,50 @@ +from odoo.tests.common import SavepointCase +from unittest import mock +import time + +class TestIrMailServer(SavepointCase): + + def test_generate_oauth2_string_token_valid(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp + 10, + } + ) + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("fake_access_token", oauth2_string) + + + def test_generate_oauth2_string_token_expire_in_less_than_5s(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp + 2, + } + ) + with mock.patch( + "odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token", + return_value=("new-access-token", now_timestamp + 60*60) + ): + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("new-access-token", oauth2_string) + + def test_generate_oauth2_string_token_expired(self): + now_timestamp = int(time.time()) + mail_server = self.env["ir.mail_server"].new( + { + "google_gmail_access_token": "fake_access_token", + "google_gmail_access_token_expiration": now_timestamp - 2, + } + ) + with mock.patch( + "odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token", + return_value=("new-access-token", now_timestamp + 60*60) + ): + oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token") + + self.assertIn("new-access-token", oauth2_string) \ No newline at end of file From 4c08e2d67ce092fe45aee865b37faff651ed6789 Mon Sep 17 00:00:00 2001 From: Pierre Verkest Date: Tue, 8 Oct 2024 16:52:52 +0200 Subject: [PATCH 2/2] [FIX] mail: ignore SMTPServerDisconnected error while smtp_session failed to connect no needs to raise error while trying to quit it --- addons/mail/models/mail_mail.py | 7 ++++++- addons/mail/tests/__init__.py | 1 + addons/mail/tests/test_mail_mail.py | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 addons/mail/tests/test_mail_mail.py diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index 2e32ce22451dd..ec5368a16e701 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -278,7 +278,12 @@ def send(self, auto_commit=False, raise_exception=False): len(batch_ids), server_id) finally: if smtp_session: - smtp_session.quit() + try: + smtp_session.quit() + except smtplib.SMTPServerDisconnected: + _logger.info( + "Ignoring SMTPServerDisconnected while trying to quit non open session" + ) def _send(self, auto_commit=False, raise_exception=False, smtp_session=None): IrMailServer = self.env['ir.mail_server'] diff --git a/addons/mail/tests/__init__.py b/addons/mail/tests/__init__.py index 7287046d6ccac..9bdf6981482d8 100644 --- a/addons/mail/tests/__init__.py +++ b/addons/mail/tests/__init__.py @@ -5,3 +5,4 @@ from . import test_mail_render from . import test_res_partner from . import test_update_notification +from . import test_mail_mail \ No newline at end of file diff --git a/addons/mail/tests/test_mail_mail.py b/addons/mail/tests/test_mail_mail.py new file mode 100644 index 0000000000000..07569c623750f --- /dev/null +++ b/addons/mail/tests/test_mail_mail.py @@ -0,0 +1,17 @@ +from odoo.tests import SavepointCase +from unittest import mock +import smtplib + + +class MailCase(SavepointCase): + + def test_mail_send_missing_not_connected(self): + """This assume while calling self.env['ir.mail_server'].connect() it return + an disconnected smtp_session which is a non falsy object value""" + + not_connected = smtplib.SMTP(local_hostname="fake-hostname.com", port=9999, timeout=1) + mail = self.env["mail.mail"].new({}) + with mock.patch("odoo.addons.base.models.ir_mail_server.IrMailServer.connect", return_value=not_connected): + mail.send() + # if we get here SMTPServerDisconnected was not raised + self.assertEqual(mail.state, "outgoing") \ No newline at end of file