Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved authenticator system #218

Merged
merged 44 commits into from
Jan 31, 2021
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b247b2f
Implement authenticator system
pepoluan Dec 14, 2020
0afce67
Add test cases
pepoluan Dec 14, 2020
be0ffd9
Update documentation
pepoluan Dec 14, 2020
c6defe9
Update NEWS
pepoluan Dec 14, 2020
33445d7
Handle symlink creation error on Windows
pepoluan Dec 21, 2020
22418ee
Silence some PyCharm warnings
pepoluan Dec 21, 2020
9a2c046
Increase loop.stop delay to 1.5
pepoluan Dec 21, 2020
413f46f
Merge branch 'master' into authenticator_see_session
pepoluan Dec 21, 2020
c1b8b70
Update comment on the GA yml file
pepoluan Dec 22, 2020
91c1287
Allow user to customize AUTH LOGIN challenges
pepoluan Dec 22, 2020
0ec5fd6
Merge branch 'master' into authenticator_see_session
pepoluan Dec 24, 2020
5684d0a
Silence code inspection warning
pepoluan Dec 24, 2020
a4f17b2
Remove unused import
pepoluan Dec 24, 2020
8096431
Add boxed_sess to also record contents of session
pepoluan Dec 24, 2020
653526c
Resplit signature line to reduce diff with master
pepoluan Dec 24, 2020
dd5627e
Remove special handling for "=" during AUTH
pepoluan Jan 15, 2021
e489f87
Adapt Test Cases to removal of "=" special handling
pepoluan Jan 15, 2021
be7cc3b
Rewrite assert_auth_* helper funcs
pepoluan Jan 15, 2021
f3872d1
Remove handling for login is None
pepoluan Jan 15, 2021
a802e4f
Update NEWS.rst
pepoluan Jan 15, 2021
bc1b7be
Update handler.rst
pepoluan Jan 15, 2021
e34b350
Restructure to silence coverage protests
pepoluan Jan 15, 2021
dcad667
Exclude from coverage because it's being deprecated
pepoluan Jan 15, 2021
43397bf
Merge branch 'bugfix_rfc4954' into authenticator_see_session
pepoluan Jan 16, 2021
501b1ef
Fix bugs introduced when merging earlier
pepoluan Jan 16, 2021
589b2c0
Use 'attrs' to enforce usage of kwargs
pepoluan Jan 16, 2021
aa2bb0a
Promote _LoginPassword to LoginPassword ...
pepoluan Jan 16, 2021
a85468e
Implement "AUTH LOGIN <username>" support
pepoluan Jan 16, 2021
e13650e
Rename "authenticator" func to "auth_callback"
pepoluan Jan 16, 2021
46f04ae
Tidy up imports
pepoluan Jan 16, 2021
98437d2
Fix: Prevent kwarg collision
pepoluan Jan 16, 2021
f4ccdbd
Fix too-long line
pepoluan Jan 16, 2021
e06145b
Fix wrong thing being tested
pepoluan Jan 16, 2021
63fbda6
Fix wrong test
pepoluan Jan 16, 2021
45bb9d3
Fix authenticator
pepoluan Jan 16, 2021
099a4da
Change piecemeal assertEqual to direct assert ==
pepoluan Jan 16, 2021
b81c560
Remove leftover misunderstanding
pepoluan Jan 16, 2021
87b153c
Encode challenge as utf8
pepoluan Jan 16, 2021
7e4d75d
Add logging (DEBUG) of challenge sent
pepoluan Jan 16, 2021
c35b6bd
Merge branch 'master' into authenticator_see_session
pepoluan Jan 19, 2021
9975f43
Merge branch 'master' into authenticator_see_session
pepoluan Jan 24, 2021
28c2459
Merge branch 'master' into authenticator_see_session
pepoluan Jan 28, 2021
c122842
Merge branch 'master' into authenticator_see_session
pepoluan Jan 29, 2021
2918c25
Merge branch 'master' into authenticator_see_session
pepoluan Jan 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions .github/workflows/unit-testing-and-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,22 @@ jobs:
include:
- os: "windows-latest"
platform: "mswin"
# Only the latest stable branch as indicated on
# https://devguide.python.org/#branchstatus
# ('stable' means Status == "security")
# Reason being that Windows users can change their Python anytime,
# so there is no benefit in testing all released Python versions.
# Plus pypy3 implementation in Windows is ... complicated. We
# should stay away from all those complications; users choosing to
# run aiosmtpd on pypy3 on Windows should be considered advanced
# and know what they're doing.
# For Windows, we'll use only the latest "frozen" branch.
# "Frozen" here means Status == "security" as indicated on the
# https://devguide.python.org/#status-of-python-branches page.
# I call it "frozen" because "no more binaries are released", so
# for the great majority of users not compiling their own CPython,
# the behavior won't change. Originally I call it "stable" but
# someone apparently defined the term "stable" as "still receiving
# bugfixes" so I had to change to prevent confusion.
#
# Also, Windows users can change their Python anytime since it's not part of the OS.
# Hence there is no benefit in testing all released Python versions.
#
# In addition, pypy3 implementation in Windows is ... complicated. We should stay
# away from all those complications; users choosing to use pypy3 on Windows to run
# aiosmtpd should be considered advanced, know what they're doing, and ready for
# all the consequences
Copy link
Collaborator Author

@pepoluan pepoluan Dec 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A looong explanation on why we use 3.7 on Windows ... and a self-made term "frozen" because Ned thinks "stable" == "still receiving bugfixes".

(I personally think "stable" == "no possible (official) behavior change", but whatevs.)

python-version: "3.7"
runs-on: ${{ matrix.os }}
steps:
Expand Down
19 changes: 19 additions & 0 deletions aiosmtpd/docs/NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
===================


1.3.0 (aiosmtpd-next-next)
==========================

Added
-----
* authenticator system improves on auth_callback by enabling the called function to see the
SMTP Session and other info. (We can deprecate auth_callback in a future version)


1.2.4 (aiosmtpd-next)
=====================

Fixed/Improved
--------------
* Remove special handling for lone ``=`` during AUTH;
it is now treated as simple Base64-encoded ``b""``.
This is the correct, strict interpretation of :rfc:`4954` mentions about ``=``


1.2.3 (2021-01-14)
==================

Expand Down
2 changes: 1 addition & 1 deletion aiosmtpd/docs/handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ those methods of the handler instance will override the built-in methods.

:boldital:`server` is the instance of the ``SMTP`` class invoking the AUTH hook.
This allows the AUTH hook implementation to invoke facilities such as the
``push()`` and ``_auth_interact()`` methods.
``push()`` and ``challenge_auth()`` methods.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed the function so it doesn't start with "_"

Had to be creative here because "auth_*" prefix has its own meaning...


:boldital:`args` is a list of string split from the string after the ``AUTH`` command.
``args[0]`` is always equal to ``MECHANISM``.
Expand Down
54 changes: 45 additions & 9 deletions aiosmtpd/docs/smtp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
The SMTP class
================

At the heart of this module is the ``SMTP`` class. This class implements the
:rfc:`5321` Simple Mail Transport
Protocol. Often you won't run an ``SMTP`` instance directly, but instead will
use a :ref:`controller <controller>` instance to run the server in a subthread.
At the heart of this module is the ``SMTP`` class.
This class implements the :rfc:`5321` Simple Mail Transport Protocol.
pepoluan marked this conversation as resolved.
Show resolved Hide resolved
Often you won't run an ``SMTP`` instance directly,
but instead will use a :ref:`Controller <controller>` instance to run the server in a subthread.

>>> from aiosmtpd.controller import Controller

Expand Down Expand Up @@ -107,7 +107,7 @@ SMTP API
decode_data=False, hostname=None, ident=None, tls_context=None, \
require_starttls=False, timeout=300, auth_required=False, \
auth_require_tls=True, auth_exclude_mechanism=None, auth_callback=None, \
command_call_limit=None, loop=None)
authenticator=None, command_call_limit=None, loop=None)

**Parameters**

Expand Down Expand Up @@ -159,6 +159,31 @@ SMTP API
``login: bytes``, and ``password: bytes``. Based on these args, the function
must return a ``bool`` that indicates whether the client's authentication
attempt is accepted/successful or not.
The** ``authenticator`` parameter below, if set, **overrides** this parameter.

:boldital:`authenticator` is a function whose signature is identical to ``aiosmtpd.smtp.AuthenticatorType``.
This parameter, if set, **overrides** the ``auth_callback`` parameter above.
The function must accept five arguments:

* ``server`` -- reference to the calling SMTP instance
* ``session`` -- the Session object of the current SMTP session
* ``envelope`` -- the Envelope object of the current SMTP session so far
* ``mechanism`` -- the SMTP Auth Mechanism chosen by the SMTP Client
* ``auth_data`` -- a data structure containing information necessary for authentication.
For built-in mechanisms this invariably contains a tuple of ``(username, password)``

The function must return an instance of ``AuthResult``,
a namedtuple with the following fields/attributes:

* ``success`` -- True if authentication successful
* ``handled`` -- (ignored if ``success`` is True)
Indicates all necessary processing (e.g., sending of SMTP Status Codes) has been handled and
the calling SMTP instance does not need to perform further processing
* ``message`` -- (Optional) Message explaining the ``success`` value.
If ``handled`` is false, then contains the SMTP Status Code to be sent by the calling SMTP instance
* ``auth_data`` -- (only if ``success`` is True)
A free-form data structure containing the authentication information.
For the built-in AUTH mechanisms, invariably contains a tuple of ``(username, password)``

:boldital:`command_call_limit` if not ``None`` sets the maximum time a certain SMTP
command can be invoked.
Expand Down Expand Up @@ -203,7 +228,7 @@ SMTP API
:boldital:`loop` is the asyncio event loop to use. If not given,
:meth:`asyncio.new_event_loop()` is called to create the event loop.

**Attributes**
**Attributes & Methods**

.. py:attribute:: line_length_limit

Expand All @@ -221,6 +246,16 @@ SMTP API
:attr:`line_length_limit` to ``2**16`` *before* instantiating the
:class:`SMTP` class.

.. py:attribute:: AuthLoginUsernameChallenge

A ``str`` containing the base64-encoded challenge to be sent as the first challenge
in the ``AUTH LOGIN`` mechanism.

.. py:attribute:: AuthLoginPasswordChallenge

A ``str`` containing the base64-encoded challenge to be sent as the second challenge
in the ``AUTH LOGIN`` mechanism.

.. attribute:: event_handler

The *handler* instance passed into the constructor.
Expand Down Expand Up @@ -301,9 +336,10 @@ SMTP API
Enabling STARTTLS
=================

To enable :rfc:`3207` ``STARTTLS``, you must supply the *tls_context* argument
to the :class:`SMTP` class. *tls_context* is created with the
:func:`ssl.create_default_context` call from the :mod:`ssl` module, as follows::
To enable :rfc:`3207` ``STARTTLS``,
you must supply the *tls_context* argument to the :class:`SMTP` class.
*tls_context* is created with the :func:`ssl.create_default_context` call
from the :mod:`ssl` module, as follows::

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)

Expand Down
Loading