diff --git a/fittrackee/emails/email.py b/fittrackee/emails/email.py index 70b00f5e..b2ff619e 100644 --- a/fittrackee/emails/email.py +++ b/fittrackee/emails/email.py @@ -4,6 +4,7 @@ import ssl from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from typing import Dict, List, Optional, Type, Union +from urllib.parse import unquote from babel.support import Translations from flask import Flask @@ -131,7 +132,7 @@ class EmailService: parsed_url = parse_url(email_url) if parsed_url.scheme != 'smtp': raise InvalidEmailUrlScheme() - credentials = ( + username, password = ( parsed_url.auth.split(':') if parsed_url.auth else [None, None] # type: ignore @@ -139,10 +140,12 @@ class EmailService: return { 'host': parsed_url.host, 'port': 25 if parsed_url.port is None else parsed_url.port, - 'use_tls': True if parsed_url.query == 'tls=True' else False, - 'use_ssl': True if parsed_url.query == 'ssl=True' else False, - 'username': credentials[0], - 'password': credentials[1], + 'use_tls': parsed_url.query == 'tls=True', + 'use_ssl': parsed_url.query == 'ssl=True', + 'username': username, + 'password': ( + unquote(password) if isinstance(password, str) else password + ), } @property diff --git a/fittrackee/tests/emails/test_email_service.py b/fittrackee/tests/emails/test_email_service.py index 25b347b6..7b0f0de7 100644 --- a/fittrackee/tests/emails/test_email_service.py +++ b/fittrackee/tests/emails/test_email_service.py @@ -1,4 +1,5 @@ from unittest.mock import Mock, patch +from urllib.parse import quote import pytest from flask import Flask @@ -43,6 +44,7 @@ class TestEmailServiceUrlParser(CallArgsMixin): @staticmethod def assert_parsed_email(url: str) -> None: parsed_email = email_service.parse_email_url(url) + assert parsed_email['username'] is None assert parsed_email['password'] is None assert parsed_email['host'] == 'localhost' @@ -60,7 +62,9 @@ class TestEmailServiceUrlParser(CallArgsMixin): def test_it_parses_email_url(self) -> None: url = 'smtp://test@example.com:12345678@localhost:25' + parsed_email = email_service.parse_email_url(url) + assert parsed_email['username'] == 'test@example.com' assert parsed_email['password'] == '12345678' assert parsed_email['host'] == 'localhost' @@ -70,7 +74,9 @@ class TestEmailServiceUrlParser(CallArgsMixin): def test_it_parses_email_url_with_tls(self) -> None: url = 'smtp://test@example.com:12345678@localhost:587?tls=True' + parsed_email = email_service.parse_email_url(url) + assert parsed_email['username'] == 'test@example.com' assert parsed_email['password'] == '12345678' assert parsed_email['host'] == 'localhost' @@ -80,7 +86,9 @@ class TestEmailServiceUrlParser(CallArgsMixin): def test_it_parses_email_url_with_ssl(self) -> None: url = 'smtp://test@example.com:12345678@localhost:465?ssl=True' + parsed_email = email_service.parse_email_url(url) + assert parsed_email['username'] == 'test@example.com' assert parsed_email['password'] == '12345678' assert parsed_email['host'] == 'localhost' @@ -88,6 +96,21 @@ class TestEmailServiceUrlParser(CallArgsMixin): assert parsed_email['use_tls'] is False assert parsed_email['use_ssl'] is True + def test_it_parses_email_url_with_encoded_password(self) -> None: + username = "user_name@example.com" + password = "passwordWith@And&And?" + encoded_password = quote(password) + url = f"smtp://{username}:{encoded_password}@localhost:465?ssl=True" + + parsed_email = email_service.parse_email_url(url) + + assert parsed_email['username'] == username + assert parsed_email['password'] == password + assert parsed_email['host'] == 'localhost' + assert parsed_email['port'] == 465 + assert parsed_email['use_tls'] is False + assert parsed_email['use_ssl'] is True + class TestEmailServiceSend(CallArgsMixin): email_data = {