Merge pull request #433 from SamR1/handle-encoded-password
Handle encoded password in EMAIL_URL
This commit is contained in:
		@@ -4,6 +4,7 @@ import ssl
 | 
				
			|||||||
from email.mime.multipart import MIMEMultipart
 | 
					from email.mime.multipart import MIMEMultipart
 | 
				
			||||||
from email.mime.text import MIMEText
 | 
					from email.mime.text import MIMEText
 | 
				
			||||||
from typing import Dict, List, Optional, Type, Union
 | 
					from typing import Dict, List, Optional, Type, Union
 | 
				
			||||||
 | 
					from urllib.parse import unquote
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from babel.support import Translations
 | 
					from babel.support import Translations
 | 
				
			||||||
from flask import Flask
 | 
					from flask import Flask
 | 
				
			||||||
@@ -131,7 +132,7 @@ class EmailService:
 | 
				
			|||||||
        parsed_url = parse_url(email_url)
 | 
					        parsed_url = parse_url(email_url)
 | 
				
			||||||
        if parsed_url.scheme != 'smtp':
 | 
					        if parsed_url.scheme != 'smtp':
 | 
				
			||||||
            raise InvalidEmailUrlScheme()
 | 
					            raise InvalidEmailUrlScheme()
 | 
				
			||||||
        credentials = (
 | 
					        username, password = (
 | 
				
			||||||
            parsed_url.auth.split(':')
 | 
					            parsed_url.auth.split(':')
 | 
				
			||||||
            if parsed_url.auth
 | 
					            if parsed_url.auth
 | 
				
			||||||
            else [None, None]  # type: ignore
 | 
					            else [None, None]  # type: ignore
 | 
				
			||||||
@@ -139,10 +140,12 @@ class EmailService:
 | 
				
			|||||||
        return {
 | 
					        return {
 | 
				
			||||||
            'host': parsed_url.host,
 | 
					            'host': parsed_url.host,
 | 
				
			||||||
            'port': 25 if parsed_url.port is None else parsed_url.port,
 | 
					            'port': 25 if parsed_url.port is None else parsed_url.port,
 | 
				
			||||||
            'use_tls': True if parsed_url.query == 'tls=True' else False,
 | 
					            'use_tls': parsed_url.query == 'tls=True',
 | 
				
			||||||
            'use_ssl': True if parsed_url.query == 'ssl=True' else False,
 | 
					            'use_ssl': parsed_url.query == 'ssl=True',
 | 
				
			||||||
            'username': credentials[0],
 | 
					            'username': username,
 | 
				
			||||||
            'password': credentials[1],
 | 
					            'password': (
 | 
				
			||||||
 | 
					                unquote(password) if isinstance(password, str) else password
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
from unittest.mock import Mock, patch
 | 
					from unittest.mock import Mock, patch
 | 
				
			||||||
 | 
					from urllib.parse import quote
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from flask import Flask
 | 
					from flask import Flask
 | 
				
			||||||
@@ -43,6 +44,7 @@ class TestEmailServiceUrlParser(CallArgsMixin):
 | 
				
			|||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def assert_parsed_email(url: str) -> None:
 | 
					    def assert_parsed_email(url: str) -> None:
 | 
				
			||||||
        parsed_email = email_service.parse_email_url(url)
 | 
					        parsed_email = email_service.parse_email_url(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert parsed_email['username'] is None
 | 
					        assert parsed_email['username'] is None
 | 
				
			||||||
        assert parsed_email['password'] is None
 | 
					        assert parsed_email['password'] is None
 | 
				
			||||||
        assert parsed_email['host'] == 'localhost'
 | 
					        assert parsed_email['host'] == 'localhost'
 | 
				
			||||||
@@ -60,7 +62,9 @@ class TestEmailServiceUrlParser(CallArgsMixin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_it_parses_email_url(self) -> None:
 | 
					    def test_it_parses_email_url(self) -> None:
 | 
				
			||||||
        url = 'smtp://test@example.com:12345678@localhost:25'
 | 
					        url = 'smtp://test@example.com:12345678@localhost:25'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parsed_email = email_service.parse_email_url(url)
 | 
					        parsed_email = email_service.parse_email_url(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert parsed_email['username'] == 'test@example.com'
 | 
					        assert parsed_email['username'] == 'test@example.com'
 | 
				
			||||||
        assert parsed_email['password'] == '12345678'
 | 
					        assert parsed_email['password'] == '12345678'
 | 
				
			||||||
        assert parsed_email['host'] == 'localhost'
 | 
					        assert parsed_email['host'] == 'localhost'
 | 
				
			||||||
@@ -70,7 +74,9 @@ class TestEmailServiceUrlParser(CallArgsMixin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_it_parses_email_url_with_tls(self) -> None:
 | 
					    def test_it_parses_email_url_with_tls(self) -> None:
 | 
				
			||||||
        url = 'smtp://test@example.com:12345678@localhost:587?tls=True'
 | 
					        url = 'smtp://test@example.com:12345678@localhost:587?tls=True'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parsed_email = email_service.parse_email_url(url)
 | 
					        parsed_email = email_service.parse_email_url(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert parsed_email['username'] == 'test@example.com'
 | 
					        assert parsed_email['username'] == 'test@example.com'
 | 
				
			||||||
        assert parsed_email['password'] == '12345678'
 | 
					        assert parsed_email['password'] == '12345678'
 | 
				
			||||||
        assert parsed_email['host'] == 'localhost'
 | 
					        assert parsed_email['host'] == 'localhost'
 | 
				
			||||||
@@ -80,7 +86,9 @@ class TestEmailServiceUrlParser(CallArgsMixin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_it_parses_email_url_with_ssl(self) -> None:
 | 
					    def test_it_parses_email_url_with_ssl(self) -> None:
 | 
				
			||||||
        url = 'smtp://test@example.com:12345678@localhost:465?ssl=True'
 | 
					        url = 'smtp://test@example.com:12345678@localhost:465?ssl=True'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parsed_email = email_service.parse_email_url(url)
 | 
					        parsed_email = email_service.parse_email_url(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert parsed_email['username'] == 'test@example.com'
 | 
					        assert parsed_email['username'] == 'test@example.com'
 | 
				
			||||||
        assert parsed_email['password'] == '12345678'
 | 
					        assert parsed_email['password'] == '12345678'
 | 
				
			||||||
        assert parsed_email['host'] == 'localhost'
 | 
					        assert parsed_email['host'] == 'localhost'
 | 
				
			||||||
@@ -88,6 +96,21 @@ class TestEmailServiceUrlParser(CallArgsMixin):
 | 
				
			|||||||
        assert parsed_email['use_tls'] is False
 | 
					        assert parsed_email['use_tls'] is False
 | 
				
			||||||
        assert parsed_email['use_ssl'] is True
 | 
					        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):
 | 
					class TestEmailServiceSend(CallArgsMixin):
 | 
				
			||||||
    email_data = {
 | 
					    email_data = {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user