17 Commits

Author SHA1 Message Date
d5a8bb3acd Advanced to version 0.5.0. 2023-01-06 00:21:19 +08:00
27d27127f6 Added the DIGEST_AUTH_REALM configuration variable as the recommended way to set the authentication realm. Changed the default realm from an empty string to "Login Required". 2023-01-06 00:20:40 +08:00
5ebdea6d0a Reordered the code in the create_app methods of the AuthenticationTestCase and FlaskLoginTestCase test cases. 2023-01-05 22:50:59 +08:00
ea31bb9579 Revised the coding style in the init_app method of the DigestAuth class. 2023-01-05 22:42:59 +08:00
4f30756dc5 Advanced to version 0.4.0. 2023-01-04 21:29:51 +08:00
cdc057f851 Renamed the package from flask-digest-auth to Flask-DigestAuth, by the Flask recommended extension guidelines https://flask.palletsprojects.com/en/latest/extensiondev/. 2023-01-04 21:29:12 +08:00
574ecade05 Revised the copyright year in auth.py. 2023-01-04 21:29:04 +08:00
84b9c5f62e Renamed digest_auth_state to _digest_auth_state as the state stored in the request instance. 2023-01-04 20:48:45 +08:00
4990de085c Changed to store the DigestAuth instance from app.digest_auth to app.extensions["digest_auth"]. 2023-01-04 20:42:51 +08:00
51e51ae4e2 Replaced auth.app with current_app. 2023-01-04 20:37:57 +08:00
2de770aed0 Advanced to version 0.3.1. 2022-12-29 23:49:28 +08:00
9ab413d583 Added the test_disabled test to the FlaskLoginTestCase test case. 2022-12-29 23:44:02 +08:00
aeb93a60e5 Fixed to store the auth state in request instead of the g global object in the flask_login load_user_from_request and unauthorized handlers in the init_app method of the DigestAuth class. This is so that the auth state is always reset in the lifecycle of request even if g stays. Revised the unauthorized to create a new auth state if it is not available in the current request, in the case that the load_user_from_request handler was not run previously. 2022-12-29 23:43:35 +08:00
a07118ef9c Revised the documentation for digest_auth parameter of the open method in the test client, to be clear. 2022-12-07 18:59:41 +08:00
514e9255aa Replaced "my_user" and "my_pass" with USERNAME and PASSWORD in the examples of the test client in the documentation, to avoid GitGuardian from detecting them as real passwords. 2022-12-07 18:55:52 +08:00
79abdc9cde Fixed the documentation of the login_required decorator in the DigestAuth class. 2022-12-07 18:48:39 +08:00
038e7a8352 Removed the warnings in the documentation of the test client. It is API document now. All content, for public or not, are available. There is no need to warn now. 2022-12-07 18:45:06 +08:00
10 changed files with 156 additions and 104 deletions

View File

@ -6,13 +6,13 @@ Flask HTTP Digest Authentication
Description
===========
*Flask-Digest-Auth* is an `HTTP Digest Authentication`_ implementation
*Flask-DigestAuth* is an `HTTP Digest Authentication`_ implementation
for Flask_ applications. It authenticates the user for the protected
views.
HTTP Digest Authentication is specified in `RFC 2617`_.
Refer to the full `Flask-Digest-Auth readthedocs documentation`_.
Refer to the full `Flask-DigestAuth readthedocs documentation`_.
Why HTTP Digest Authentication?
@ -30,7 +30,7 @@ own challenge-response log in form, but then you are reinventing the
wheels. If a pretty log in form is not critical to your project, HTTP
Digest Authentication should be a good choice.
Flask-Digest-Auth works with Flask-Login_. Log in protection can be
Flask-DigestAuth works with Flask-Login_. Log in protection can be
separated with the authentication mechanism. You can create protected
Flask modules without knowing the actual authentication mechanisms.
@ -38,18 +38,25 @@ Flask modules without knowing the actual authentication mechanisms.
Installation
============
You can install Flask-Digest-Auth with ``pip``:
You can install Flask-DigestAuth with ``pip``:
::
pip install Flask-Digest-Auth
pip install Flask-DigestAuth
You may also install the latest source from the
`Flask-Digest-Auth GitHub repository`_.
`Flask-DigestAuth GitHub repository`_.
::
pip install git+https://github.com/imacat/flask-digest-auth.git
pip install git+https://github.com/imacat/flask-digestauth.git
Configuration
=============
Flask-DigestAuth takes the configuration ``DIGEST_AUTH_REALM`` as the
realm. The default realm is ``Login Required``.
Setting the Password
@ -70,14 +77,14 @@ you need to ask their password, to generate and store the new password
hash.
Flask-Digest-Auth Alone
=======================
Flask-DigestAuth Alone
======================
Flask-Digest-Auth can authenticate the users alone.
Flask-DigestAuth can authenticate the users alone.
Simple Applications with Flask-Digest-Auth Alone
------------------------------------------------
Simple Applications with Flask-DigestAuth Alone
-----------------------------------------------
In your ``my_app.py``:
@ -89,7 +96,7 @@ In your ``my_app.py``:
app: flask = Flask(__name__)
... (Configure the Flask application) ...
auth: DigestAuth = DigestAuth(realm="Admin")
auth: DigestAuth = DigestAuth()
auth.init_app(app)
@auth.register_get_password
@ -112,8 +119,8 @@ In your ``my_app.py``:
return redirect(request.form.get("next"))
Larger Applications with ``create_app()`` with Flask-Digest-Auth Alone
----------------------------------------------------------------------
Larger Applications with ``create_app()`` with Flask-DigestAuth Alone
---------------------------------------------------------------------
In your ``my_app/__init__.py``:
@ -128,7 +135,6 @@ In your ``my_app/__init__.py``:
app: flask = Flask(__name__)
... (Configure the Flask application) ...
auth.realm = app.config["REALM"]
auth.init_app(app)
@auth.register_get_password
@ -169,19 +175,19 @@ In your ``my_app/views.py``:
Flask-Login Integration
=======================
Flask-Digest-Auth works with Flask-Login_. You can write a Flask
Flask-DigestAuth works with Flask-Login_. You can write a Flask
module that requires log in, without specifying how to log in. The
application can use either HTTP Digest Authentication, or the log in
forms, as needed.
To use Flask-Login with Flask-Digest-Auth,
To use Flask-Login with Flask-DigestAuth,
``login_manager.init_app(app)`` must be called before
``auth.init_app(app)``.
The currently logged-in user can be retrieved at
``flask_login.current_user``, if any.
The views only depend on Flask-Login, but not the Flask-Digest-Auth.
The views only depend on Flask-Login, but not the Flask-DigestAuth.
You can change the actual authentication mechanism without changing
the views.
@ -207,7 +213,7 @@ In your ``my_app.py``:
def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ...
auth: DigestAuth = DigestAuth(realm="Admin")
auth: DigestAuth = DigestAuth()
auth.init_app(app)
@auth.register_get_password
@ -251,7 +257,6 @@ In your ``my_app/__init__.py``:
def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ...
auth.realm = app.config["REALM"]
auth.init_app(app)
@auth.register_get_password
@ -293,7 +298,7 @@ mechanism without changing the views.
Session Integration
===================
Flask-Digest-Auth features session integration. The user log in
Flask-DigestAuth features session integration. The user log in
is remembered in the session. The authentication information is not
requested again. This is different to the practice of the HTTP Digest
Authentication, but is convenient for the log in accounting.
@ -315,14 +320,14 @@ logging the log in event, adding the log in counter, etc.
Log Out
=======
Flask-Digest-Auth supports log out. The user will be prompted for the
Flask-DigestAuth supports log out. The user will be prompted for the
new username and password.
Test Client
===========
Flask-Digest-Auth comes with a test client that supports HTTP digest
Flask-DigestAuth comes with a test client that supports HTTP digest
authentication.
@ -340,8 +345,9 @@ A unittest Test Case
def create_app(self):
app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": "admin",
})
app.test_client_class = Client
return app
@ -350,7 +356,7 @@ A unittest Test Case
response = self.client.get("/admin")
self.assertEqual(response.status_code, 401)
response = self.client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
self.assertEqual(response.status_code, 200)
@ -367,8 +373,9 @@ A pytest Test
@pytest.fixture()
def app():
app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": "admin",
})
app.test_client_class = Client
yield app
@ -382,14 +389,14 @@ A pytest Test
response = client.get("/admin")
assert response.status_code == 401
response = client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
assert response.status_code == 200
Copyright
=========
Copyright (c) 2022 imacat.
Copyright (c) 2022-2023 imacat.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -414,6 +421,6 @@ Authors
.. _HTTP Digest Authentication: https://en.wikipedia.org/wiki/Digest_access_authentication
.. _RFC 2617: https://www.rfc-editor.org/rfc/rfc2617
.. _Flask: https://flask.palletsprojects.com
.. _Flask-Digest-Auth GitHub repository: https://github.com/imacat/flask-digest-auth
.. _Flask-Digest-Auth readthedocs documentation: https://flask-digest-auth.readthedocs.io
.. _Flask-DigestAuth GitHub repository: https://github.com/imacat/flask-digestauth
.. _Flask-DigestAuth readthedocs documentation: https://flask-digestauth.readthedocs.io
.. _Flask-Login: https://flask-login.readthedocs.io

View File

@ -10,10 +10,10 @@ sys.path.insert(0, os.path.abspath('../../src/'))
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'Flask-Digest-Auth'
copyright = '2022, imacat'
project = 'Flask-DigestAuth'
copyright = '2022-2023, imacat'
author = 'imacat'
release = '0.3.0'
release = '0.5.0'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

View File

@ -4,8 +4,8 @@ Examples
.. _example-alone-simple:
Simple Applications with Flask-Digest-Auth Alone
------------------------------------------------
Simple Applications with Flask-DigestAuth Alone
-----------------------------------------------
In your ``my_app.py``:
@ -17,7 +17,7 @@ In your ``my_app.py``:
app: flask = Flask(__name__)
... (Configure the Flask application) ...
auth: DigestAuth = DigestAuth(realm="Admin")
auth: DigestAuth = DigestAuth()
auth.init_app(app)
@auth.register_get_password
@ -42,8 +42,8 @@ In your ``my_app.py``:
.. _example-alone-large:
Larger Applications with ``create_app()`` with Flask-Digest-Auth Alone
----------------------------------------------------------------------
Larger Applications with ``create_app()`` with Flask-DigestAuth Alone
---------------------------------------------------------------------
In your ``my_app/__init__.py``:
@ -58,7 +58,6 @@ In your ``my_app/__init__.py``:
app: flask = Flask(__name__)
... (Configure the Flask application) ...
auth.realm = app.config["REALM"]
auth.init_app(app)
@auth.register_get_password
@ -118,7 +117,7 @@ In your ``my_app.py``:
def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ...
auth: DigestAuth = DigestAuth(realm="Admin")
auth: DigestAuth = DigestAuth()
auth.init_app(app)
@auth.register_get_password
@ -164,7 +163,6 @@ In your ``my_app/__init__.py``:
def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ...
auth.realm = app.config["REALM"]
auth.init_app(app)
@auth.register_get_password
@ -219,8 +217,9 @@ A unittest Test Case
def create_app(self):
app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": "admin",
})
app.test_client_class = Client
return app
@ -229,7 +228,7 @@ A unittest Test Case
response = self.client.get("/admin")
self.assertEqual(response.status_code, 401)
response = self.client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
self.assertEqual(response.status_code, 200)
@ -249,8 +248,9 @@ A pytest Test
@pytest.fixture()
def app():
app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": "admin",
})
app.test_client_class = Client
yield app
@ -264,5 +264,5 @@ A pytest Test
response = client.get("/admin")
assert response.status_code == 401
response = client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
assert response.status_code == 200

View File

@ -1,12 +1,12 @@
.. Flask-Digest-Auth documentation master file, created by
.. Flask-DigestAuth documentation master file, created by
sphinx-quickstart on Wed Dec 7 09:40:48 2022.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Flask-Digest-Auth's documentation!
=============================================
Welcome to Flask-DigestAuth's documentation!
============================================
*Flask-Digest-Auth* is an `HTTP Digest Authentication`_ implementation
*Flask-DigestAuth* is an `HTTP Digest Authentication`_ implementation
for Flask_ applications. It authenticates the user for the protected
views.

View File

@ -2,7 +2,7 @@ Introduction
============
*Flask-Digest-Auth* is an `HTTP Digest Authentication`_ implementation
*Flask-DigestAuth* is an `HTTP Digest Authentication`_ implementation
for Flask_ applications. It authenticates the user for the protected
views.
@ -24,7 +24,7 @@ own challenge-response log in form, but then you are reinventing the
wheels. If a pretty log in form is not critical to your project, HTTP
Digest Authentication should be a good choice.
Flask-Digest-Auth works with Flask-Login_. Log in protection can be
Flask-DigestAuth works with Flask-Login_. Log in protection can be
separated with the authentication mechanism. You can create protected
Flask modules without knowing the actual authentication mechanisms.
@ -32,18 +32,25 @@ Flask modules without knowing the actual authentication mechanisms.
Installation
------------
You can install Flask-Digest-Auth with ``pip``:
You can install Flask-DigestAuth with ``pip``:
::
pip install Flask-Digest-Auth
pip install Flask-DigestAuth
You may also install the latest source from the
`Flask-Digest-Auth GitHub repository`_.
`Flask-DigestAuth GitHub repository`_.
::
pip install git+https://github.com/imacat/flask-digest-auth.git
pip install git+https://github.com/imacat/flask-digestauth.git
Configuration
-------------
Flask-DigestAuth takes the configuration ``DIGEST_AUTH_REALM`` as the
realm. The default realm is ``Login Required``.
Setting the Password
@ -66,10 +73,10 @@ hash.
See :func:`flask_digest_auth.algo.make_password_hash`.
Flask-Digest-Auth Alone
-----------------------
Flask-DigestAuth Alone
----------------------
Flask-Digest-Auth can authenticate the users alone.
Flask-DigestAuth can authenticate the users alone.
See :ref:`example-alone-simple` and :ref:`example-alone-large`.
@ -77,12 +84,12 @@ See :ref:`example-alone-simple` and :ref:`example-alone-large`.
Flask-Login Integration
-----------------------
Flask-Digest-Auth works with Flask-Login_. You can write a Flask
Flask-DigestAuth works with Flask-Login_. You can write a Flask
module that requires log in, without specifying how to log in. The
application can use either HTTP Digest Authentication, or the log in
forms, as needed.
To use Flask-Login with Flask-Digest-Auth,
To use Flask-Login with Flask-DigestAuth,
``login_manager.init_app(app)`` must be called before
``auth.init_app(app)``.
@ -92,7 +99,7 @@ The currently logged-in user can be retrieved at
See :ref:`example-flask-login-simple` and
:ref:`example-flask-login-large`.
The views only depend on Flask-Login, but not the Flask-Digest-Auth.
The views only depend on Flask-Login, but not the Flask-DigestAuth.
You can change the actual authentication mechanism without changing
the views.
@ -100,7 +107,7 @@ the views.
Session Integration
-------------------
Flask-Digest-Auth features session integration. The user log in
Flask-DigestAuth features session integration. The user log in
is remembered in the session. The authentication information is not
requested again. This is different to the practice of the HTTP Digest
Authentication, but is convenient for the log in accounting.
@ -124,7 +131,7 @@ See :meth:`flask_digest_auth.auth.DigestAuth.register_on_login`.
Log Out
-------
Flask-Digest-Auth supports log out. The user will be prompted for the
Flask-DigestAuth supports log out. The user will be prompted for the
new username and password.
See :meth:`flask_digest_auth.auth.DigestAuth.logout`.
@ -133,7 +140,7 @@ See :meth:`flask_digest_auth.auth.DigestAuth.logout`.
Test Client
-----------
Flask-Digest-Auth comes with a test client that supports HTTP digest
Flask-DigestAuth comes with a test client that supports HTTP digest
authentication.
See :class:`flask_digest_auth.test.Client`.
@ -145,4 +152,4 @@ Also see :ref:`example-unittest` and :ref:`example-pytest`.
.. _RFC 2617: https://www.rfc-editor.org/rfc/rfc2617
.. _Flask: https://flask.palletsprojects.com
.. _Flask-Login: https://flask-login.readthedocs.io
.. _Flask-Digest-Auth GitHub repository: https://github.com/imacat/flask-digest-auth
.. _Flask-DigestAuth GitHub repository: https://github.com/imacat/flask-digestauth

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/11/23
# Copyright (c) 2022 imacat.
# Copyright (c) 2022-2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -16,16 +16,16 @@
# limitations under the License.
[metadata]
name = flask-digest-auth
version = 0.3.0
name = Flask-DigestAuth
version = 0.5.0
author = imacat
author_email = imacat@mail.imacat.idv.tw
description = The Flask HTTP Digest Authentication project.
long_description = file: README.rst
long_description_content_type = text/x-rst
url = https://github.com/imacat/flask-digest-auth
url = https://github.com/imacat/flask-digestauth
project_urls =
Bug Tracker = https://github.com/imacat/flask-digest-auth/issues
Bug Tracker = https://github.com/imacat/flask-digestauth/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: Apache Software License

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/10/22
# Copyright (c) 2022 imacat.
# Copyright (c) 2022-2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -27,7 +27,8 @@ import typing as t
from functools import wraps
from secrets import token_urlsafe, randbits
from flask import g, request, Response, session, abort, Flask, Request
from flask import g, request, Response, session, abort, Flask, Request, \
current_app
from itsdangerous import URLSafeTimedSerializer, BadData
from werkzeug.datastructures import Authorization
@ -45,8 +46,8 @@ class DigestAuth:
self.__serializer: URLSafeTimedSerializer \
= URLSafeTimedSerializer(token_urlsafe(32))
"""The serializer to generate and validate the nonce and opaque."""
self.realm: str = "" if realm is None else realm
"""The realm. Default is an empty string."""
self.realm: str = "Login Required" if realm is None else realm
"""The realm. Default is "Login Required"."""
self.algorithm: t.Optional[t.Literal["MD5", "MD5-sess"]] = None
"""The algorithm, either None, ``MD5``, or ``MD5-sess``. Default is
None."""
@ -59,8 +60,6 @@ class DigestAuth:
= ["auth", "auth-int"]
"""A list of supported quality of protection supported, either
``qop``, ``auth-int``, both, or empty. Default is both."""
self.app: t.Optional[Flask] = None
"""The current Flask application."""
self.__get_password_hash: BasePasswordHashGetter \
= BasePasswordHashGetter()
"""The callback to return the password hash."""
@ -70,7 +69,7 @@ class DigestAuth:
"""The callback to run when the user logs in."""
def login_required(self, view) -> t.Callable:
"""The view decorator for HTTP digest authentication.
"""The view decorator for the HTTP digest authentication.
:Example:
@ -328,7 +327,8 @@ class DigestAuth:
self.__on_login = OnLogInCallback()
def init_app(self, app: Flask) -> None:
"""Initializes the Flask application.
"""Initializes the Flask application. The DigestAuth instance will
be stored in ``app.extensions["digest_auth"]``.
:Example:
@ -342,8 +342,9 @@ class DigestAuth:
:param app: The Flask application.
:return: None.
"""
app.digest_auth = self
self.app = app
app.extensions["digest_auth"] = self
if "DIGEST_AUTH_REALM" in app.config:
self.realm = app.config["DIGEST_AUTH_REALM"]
if hasattr(app, "login_manager"):
from flask_login import LoginManager, login_user
@ -356,10 +357,13 @@ class DigestAuth:
:return: None.
"""
state: AuthState = getattr(request, "_digest_auth_state") \
if hasattr(request, "_digest_auth_state") \
else AuthState()
response: Response = Response()
response.status = 401
response.headers["WWW-Authenticate"] \
= self.__make_response_header(g.digest_auth_state)
= self.__make_response_header(state)
abort(response)
@login_manager.request_loader
@ -370,7 +374,7 @@ class DigestAuth:
:return: The authenticated user, or None if the
authentication fails
"""
g.digest_auth_state = AuthState()
request._digest_auth_state = AuthState()
authorization: Authorization = req.authorization
try:
if authorization is None:
@ -378,9 +382,8 @@ class DigestAuth:
if authorization.type != "digest":
raise UnauthorizedException(
"Not an HTTP digest authorization")
self.__authenticate(g.digest_auth_state)
user = login_manager.user_callback(
authorization.username)
self.__authenticate(request._digest_auth_state)
user = login_manager.user_callback(authorization.username)
login_user(user)
self.__on_login(user)
return user
@ -409,7 +412,7 @@ class DigestAuth:
if "user" in session:
del session["user"]
try:
if hasattr(self.app, "login_manager"):
if hasattr(current_app, "login_manager"):
from flask_login import logout_user
logout_user()
except ModuleNotFoundError:

View File

@ -51,7 +51,7 @@ class Client(WerkzeugClient):
response = self.client.get("/admin")
self.assertEqual(response.status_code, 401)
response = self.client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
self.assertEqual(response.status_code, 200)
For pytest_:
@ -76,7 +76,7 @@ class Client(WerkzeugClient):
response = client.get("/admin")
assert response.status_code == 401
response = client.get(
"/admin", digest_auth=("my_name", "my_pass"))
"/admin", digest_auth=(USERNAME, PASSWORD))
assert response.status_code == 200
.. _unittest: https://docs.python.org/3/library/unittest.html
@ -87,12 +87,8 @@ class Client(WerkzeugClient):
**kwargs) -> TestResponse:
"""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 digest_auth: A tuple of the username and password for the HTTP
:param digest_auth: The (*username*, *password*) tuple for the HTTP
digest authentication.
:param kwargs: The keyword arguments.
:return: The response.
@ -115,9 +111,6 @@ class Client(WerkzeugClient):
username: str, password: str) -> Authorization:
"""Composes and returns the request authorization.
.. warning::
This method is not for public.
:param www_authenticate: The ``WWW-Authenticate`` response.
:param uri: The request URI.
:param username: The username.

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/10/22
# Copyright (c) 2022 imacat.
# Copyright (c) 2022-2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -57,12 +57,13 @@ class AuthenticationTestCase(TestCase):
"""
app: Flask = Flask(__name__)
app.config.from_mapping({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": _REALM,
})
app.test_client_class = Client
auth: DigestAuth = DigestAuth(realm=_REALM)
auth: DigestAuth = DigestAuth()
auth.init_app(app)
self.user: User = User(_USERNAME, _PASSWORD)
user_db: t.Dict[str, User] = {_USERNAME: self.user}

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/11/23
# Copyright (c) 2022 imacat.
# Copyright (c) 2022-2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -45,7 +45,6 @@ class User:
self.password_hash: str = make_password_hash(
_REALM, username, password)
self.visits: int = 0
self.is_authenticated: bool = True
self.is_active: bool = True
self.is_anonymous: bool = False
@ -57,6 +56,16 @@ class User:
"""
return self.username
@property
def is_authenticated(self) -> bool:
"""Returns whether the user is authenticated.
This is required by Flask-Login.
This should return self.is_active.
:return: True if the user is active, or False otherwise.
"""
return self.is_active
class FlaskLoginTestCase(TestCase):
"""The test case with the Flask-Login integration."""
@ -68,8 +77,9 @@ class FlaskLoginTestCase(TestCase):
"""
app: Flask = Flask(__name__)
app.config.from_mapping({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
"DIGEST_AUTH_REALM": _REALM,
})
app.test_client_class = Client
@ -83,7 +93,7 @@ class FlaskLoginTestCase(TestCase):
login_manager: flask_login.LoginManager = flask_login.LoginManager()
login_manager.init_app(app)
auth: DigestAuth = DigestAuth(realm=_REALM)
auth: DigestAuth = DigestAuth()
auth.init_app(app)
self.user: User = User(_USERNAME, _PASSWORD)
@ -256,3 +266,34 @@ class FlaskLoginTestCase(TestCase):
response = self.client.get(admin_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(self.user.visits, 2)
def test_disabled(self) -> None:
"""Tests the disabled user.
:return: None.
"""
if not self.has_flask_login:
self.skipTest("Skipped without Flask-Login.")
response: Response
self.user.is_active = False
response = self.client.get(self.app.url_for("admin-1"))
self.assertEqual(response.status_code, 401)
response = self.client.get(self.app.url_for("admin-1"),
digest_auth=(_USERNAME, _PASSWORD))
self.assertEqual(response.status_code, 401)
self.user.is_active = True
response = self.client.get(self.app.url_for("admin-1"),
digest_auth=(_USERNAME, _PASSWORD))
self.assertEqual(response.status_code, 200)
response = self.client.get(self.app.url_for("admin-1"))
self.assertEqual(response.status_code, 200)
self.user.is_active = False
response = self.client.get(self.app.url_for("admin-1"))
self.assertEqual(response.status_code, 401)
response = self.client.get(self.app.url_for("admin-1"),
digest_auth=(_USERNAME, _PASSWORD))
self.assertEqual(response.status_code, 401)