Compare commits

...

3 Commits

5 changed files with 27 additions and 14 deletions

View File

@ -22,7 +22,7 @@ copyright = '2022, imacat'
author = 'imacat' author = 'imacat'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = '0.2.4' release = '0.3.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View File

@ -17,7 +17,7 @@
[metadata] [metadata]
name = flask-digest-auth name = flask-digest-auth
version = 0.2.4 version = 0.3.0
author = imacat author = imacat
author_email = imacat@mail.imacat.idv.tw author_email = imacat@mail.imacat.idv.tw
description = The Flask HTTP Digest Authentication project. description = The Flask HTTP Digest Authentication project.

View File

@ -28,7 +28,7 @@ def make_password_hash(realm: str, username: str, password: str) -> str:
"""Calculates the password hash for the HTTP digest authentication. """Calculates the password hash for the HTTP digest authentication.
Use this function to set the password for the user. Use this function to set the password for the user.
For example: :Example:
:: ::
@ -54,7 +54,7 @@ def calc_response(
:param uri: The request URI. :param uri: The request URI.
:param password_hash: The password hash for the HTTP digest authentication. :param password_hash: The password hash for the HTTP digest authentication.
:param nonce: The nonce. :param nonce: The nonce.
:param qop: the quality of protection, either ``auth`` or ``auth-int``. :param qop: The quality of protection, either ``auth`` or ``auth-int``.
:param algorithm: The algorithm, either ``MD5`` or ``MD5-sess``. :param algorithm: The algorithm, either ``MD5`` or ``MD5-sess``.
:param cnonce: The client nonce, which must exists when qop exists or :param cnonce: The client nonce, which must exists when qop exists or
algorithm is ``MD5-sess``. algorithm is ``MD5-sess``.

View File

@ -45,7 +45,7 @@ class DigestAuth:
= URLSafeTimedSerializer(token_urlsafe(32)) = URLSafeTimedSerializer(token_urlsafe(32))
self.realm: str = "" if realm is None else realm self.realm: str = "" if realm is None else realm
"""The realm. Default is an empty string.""" """The realm. Default is an empty string."""
self.algorithm: t.Optional[str] = None self.algorithm: t.Optional[t.Literal["MD5", "MD5-sess"]] = None
"""The algorithm, either None, ``MD5``, or ``MD5-sess``. Default is """The algorithm, either None, ``MD5``, or ``MD5-sess``. Default is
None.""" None."""
self.use_opaque: bool = True self.use_opaque: bool = True
@ -53,7 +53,8 @@ class DigestAuth:
self.__domain: t.List[str] = [] self.__domain: t.List[str] = []
"""A list of directories that this username and password applies to. """A list of directories that this username and password applies to.
Default is empty.""" Default is empty."""
self.__qop: t.List[str] = ["auth", "auth-int"] self.__qop: t.List[t.Literal["auth", "auth-int"]] \
= ["auth", "auth-int"]
"""A list of supported quality of protection supported, either """A list of supported quality of protection supported, either
``qop``, ``auth-int``, both, or empty. Default is both.""" ``qop``, ``auth-int``, both, or empty. Default is both."""
self.app: t.Optional[Flask] = None self.app: t.Optional[Flask] = None
@ -66,7 +67,7 @@ class DigestAuth:
def login_required(self, view) -> t.Callable: def login_required(self, view) -> t.Callable:
"""The view decorator for HTTP digest authentication. """The view decorator for HTTP digest authentication.
For example: :Example:
:: ::
@ -233,7 +234,7 @@ class DigestAuth:
-> None: -> None:
"""The decorator to register the callback to obtain the password hash. """The decorator to register the callback to obtain the password hash.
For example: :Example:
:: ::
@ -265,7 +266,7 @@ class DigestAuth:
-> None: -> None:
"""The decorator to register the callback to obtain the user. """The decorator to register the callback to obtain the user.
For example: :Example:
:: ::
@ -295,7 +296,7 @@ class DigestAuth:
def register_on_login(self, func: t.Callable[[t.Any], None]) -> None: def register_on_login(self, func: t.Callable[[t.Any], None]) -> None:
"""The decorator to register the callback to run when the user logs in. """The decorator to register the callback to run when the user logs in.
For example: :Example:
:: ::
@ -324,7 +325,7 @@ class DigestAuth:
def init_app(self, app: Flask) -> None: def init_app(self, app: Flask) -> None:
"""Initializes the Flask application. """Initializes the Flask application.
For example: :Example:
:: ::
@ -388,7 +389,7 @@ class DigestAuth:
This actually causes the next authentication to fail, which forces This actually causes the next authentication to fail, which forces
the browser to ask the user for the username and password again. the browser to ask the user for the username and password again.
For example: :Example:
:: ::

View File

@ -31,7 +31,9 @@ from flask_digest_auth.algo import calc_response, make_password_hash
class Client(WerkzeugClient): class Client(WerkzeugClient):
"""The test client with HTTP digest authentication enabled. """The test client with HTTP digest authentication enabled.
For unittest example: :Example:
For unittest_:
:: ::
@ -52,7 +54,7 @@ class Client(WerkzeugClient):
"/admin", digest_auth=("my_name", "my_pass")) "/admin", digest_auth=("my_name", "my_pass"))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
For pytest example: For pytest_:
:: ::
@ -76,12 +78,19 @@ class Client(WerkzeugClient):
response = client.get( response = client.get(
"/admin", digest_auth=("my_name", "my_pass")) "/admin", digest_auth=("my_name", "my_pass"))
assert response.status_code == 200 assert response.status_code == 200
.. _unittest: https://docs.python.org/3/library/unittest.html
.. _pytest: https://pytest.org
""" """
def open(self, *args, digest_auth: t.Optional[t.Tuple[str, str]] = None, def open(self, *args, digest_auth: t.Optional[t.Tuple[str, str]] = None,
**kwargs) -> TestResponse: **kwargs) -> TestResponse:
"""Opens a request. """Opens a request.
.. warning::
This is to override the parent ``open`` method. You should call
the ``get``, ``post``, ``put``, and ``delete`` methods instead.
:param args: The arguments. :param args: The arguments.
:param digest_auth: A tuple of the username and password for the HTTP :param digest_auth: A tuple of the username and password for the HTTP
digest authentication. digest authentication.
@ -106,6 +115,9 @@ class Client(WerkzeugClient):
username: str, password: str) -> Authorization: username: str, password: str) -> Authorization:
"""Composes and returns the request authorization. """Composes and returns the request authorization.
.. warning::
This method is not for public.
:param www_authenticate: The ``WWW-Authenticate`` response. :param www_authenticate: The ``WWW-Authenticate`` response.
:param uri: The request URI. :param uri: The request URI.
:param username: The username. :param username: The username.