API - allow EMAIL_URL without authentication - fix #127

This commit is contained in:
Sam 2022-01-01 11:04:08 +01:00
parent 9e683653d8
commit 33fde0394a
10 changed files with 62 additions and 8 deletions

View File

@ -18,7 +18,7 @@ export DATABASE_TEST_URL=postgresql://fittrackee:fittrackee@fittrackee-db:5432/f
export UI_URL=http://0.0.0.0:5000 export UI_URL=http://0.0.0.0:5000
# For development: # For development:
# export UI_URL=http://0.0.0.0:3000 # export UI_URL=http://0.0.0.0:3000
export EMAIL_URL=smtp://none:none@mail:1025 export EMAIL_URL=smtp://mail:1025
export SENDER_EMAIL=fittrackee@example.com export SENDER_EMAIL=fittrackee@example.com
export REDIS_URL=redis://redis:6379 export REDIS_URL=redis://redis:6379
export WORKERS_PROCESSES=2 export WORKERS_PROCESSES=2

View File

@ -209,6 +209,9 @@ To send emails, a valid ``EMAIL_URL`` must be provided:
- with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True`` - with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True``
- with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True`` - with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True``
.. versionadded:: 0.5.3
Credentials can be omitted: ``smtp://smtp.example.com:25``
Map tile server Map tile server
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -458,6 +458,10 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
<li><p>with SSL: <code class="docutils literal notranslate"><span class="pre">smtp://username:password&#64;smtp.example.com:465/?ssl=True</span></code></p></li> <li><p>with SSL: <code class="docutils literal notranslate"><span class="pre">smtp://username:password&#64;smtp.example.com:465/?ssl=True</span></code></p></li>
<li><p>with STARTTLS: <code class="docutils literal notranslate"><span class="pre">smtp://username:password&#64;smtp.example.com:587/?tls=True</span></code></p></li> <li><p>with STARTTLS: <code class="docutils literal notranslate"><span class="pre">smtp://username:password&#64;smtp.example.com:587/?tls=True</span></code></p></li>
</ul> </ul>
<div class="versionadded">
<p><span class="versionmodified added">New in version 0.5.3.</span></p>
</div>
<p>Credentials can be omitted: <code class="docutils literal notranslate"><span class="pre">smtp://smtp.example.com:25</span></code></p>
</section> </section>
<section id="map-tile-server"> <section id="map-tile-server">
<h3>Map tile server<a class="headerlink" href="#map-tile-server" title="Permalink to this headline"></a></h3> <h3>Map tile server<a class="headerlink" href="#map-tile-server" title="Permalink to this headline"></a></h3>

File diff suppressed because one or more lines are too long

View File

@ -209,6 +209,9 @@ To send emails, a valid ``EMAIL_URL`` must be provided:
- with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True`` - with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True``
- with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True`` - with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True``
.. versionadded:: 0.5.3
Credentials can be omitted: ``smtp://smtp.example.com:25``
Map tile server Map tile server
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^

View File

@ -110,6 +110,7 @@ class Email:
with self.smtp( with self.smtp(
self.host, self.port, **connection_params # type: ignore self.host, self.port, **connection_params # type: ignore
) as smtp: ) as smtp:
if self.username and self.password:
smtp.login(self.username, self.password) # type: ignore smtp.login(self.username, self.password) # type: ignore
if self.use_tls: if self.use_tls:
smtp.starttls(context=context) smtp.starttls(context=context)

View File

@ -9,7 +9,9 @@ def parse_email_url(email_url: str) -> Dict:
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 = parsed_url.auth.split(':') credentials = (
parsed_url.auth.split(':') if parsed_url.auth else [None, None]
)
return { return {
'host': parsed_url.host, 'host': parsed_url.host,
'port': parsed_url.port, 'port': parsed_url.port,

View File

@ -63,7 +63,8 @@ class TestEmailSending(CallArgsMixin):
) )
smtp = mock_smtp.return_value.__enter__.return_value smtp = mock_smtp.return_value.__enter__.return_value
assert smtp.starttls.not_called assert smtp.login.call_count == 1
smtp.starttls.assert_not_called()
self.assert_smtp(smtp) self.assert_smtp(smtp)
@patch('smtplib.SMTP_SSL') @patch('smtplib.SMTP_SSL')
@ -79,7 +80,8 @@ class TestEmailSending(CallArgsMixin):
) )
smtp = mock_smtp_ssl.return_value.__enter__.return_value smtp = mock_smtp_ssl.return_value.__enter__.return_value
assert smtp.starttls.not_called assert smtp.login.call_count == 1
smtp.starttls.assert_not_called()
self.assert_smtp(smtp) self.assert_smtp(smtp)
@patch('smtplib.SMTP_SSL') @patch('smtplib.SMTP_SSL')
@ -95,5 +97,24 @@ class TestEmailSending(CallArgsMixin):
) )
smtp = mock_smtp.return_value.__enter__.return_value smtp = mock_smtp.return_value.__enter__.return_value
assert smtp.login.call_count == 1
assert smtp.starttls.call_count == 1 assert smtp.starttls.call_count == 1
self.assert_smtp(smtp) self.assert_smtp(smtp)
@patch('smtplib.SMTP_SSL')
@patch('smtplib.SMTP')
def test_it_sends_message_without_authentication(
self, mock_smtp: Mock, mock_smtp_ssl: Mock, app_wo_email_auth: Flask
) -> None:
email_service.send(
template='password_reset_request',
lang='en',
recipient='test@test.com',
data=self.email_data,
)
smtp = mock_smtp.return_value.__enter__.return_value
smtp.login.assert_not_called()
smtp.starttls.assert_not_called()
self.assert_smtp(smtp)

View File

@ -12,6 +12,16 @@ class TestEmailUrlParser:
with pytest.raises(InvalidEmailUrlScheme): with pytest.raises(InvalidEmailUrlScheme):
parse_email_url(url) parse_email_url(url)
def test_it_parses_email_url_without_authentication(self) -> None:
url = 'smtp://localhost:25'
parsed_email = parse_email_url(url)
assert parsed_email['username'] is None
assert parsed_email['password'] is None
assert parsed_email['host'] == 'localhost'
assert parsed_email['port'] == 25
assert parsed_email['use_tls'] is False
assert parsed_email['use_ssl'] is False
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 = parse_email_url(url) parsed_email = parse_email_url(url)

View File

@ -125,13 +125,23 @@ def app_no_config() -> Generator:
@pytest.fixture @pytest.fixture
def app_ssl(monkeypatch: pytest.MonkeyPatch) -> Generator: def app_ssl(monkeypatch: pytest.MonkeyPatch) -> Generator:
monkeypatch.setenv('EMAIL_URL', 'smtp://none:none@0.0.0.0:1025?ssl=True') monkeypatch.setenv(
'EMAIL_URL', 'smtp://username:password@0.0.0.0:1025?ssl=True'
)
yield from get_app(with_config=True) yield from get_app(with_config=True)
@pytest.fixture @pytest.fixture
def app_tls(monkeypatch: pytest.MonkeyPatch) -> Generator: def app_tls(monkeypatch: pytest.MonkeyPatch) -> Generator:
monkeypatch.setenv('EMAIL_URL', 'smtp://none:none@0.0.0.0:1025?tls=True') monkeypatch.setenv(
'EMAIL_URL', 'smtp://username:password@0.0.0.0:1025?tls=True'
)
yield from get_app(with_config=True)
@pytest.fixture
def app_wo_email_auth(monkeypatch: pytest.MonkeyPatch) -> Generator:
monkeypatch.setenv('EMAIL_URL', 'smtp://0.0.0.0:1025')
yield from get_app(with_config=True) yield from get_app(with_config=True)