7 Commits

12 changed files with 118 additions and 84 deletions

40
.readthedocs.yaml Normal file
View File

@ -0,0 +1,40 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/5
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.7"
# Build documentation in the docs/ directory with Sphinx
# If using Sphinx, optionally build your docs in additional formats such as PDF
formats: all
# Optionally declare the Python requirements required to build your docs
python:
install:
- method: pip
path: .

View File

@ -15,8 +15,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
include docs/* recursive-include docs *
include docs/source/* recursive-exclude docs/build *
include docs/source/_static/* recursive-include tests *
include docs/source/_templates/* recursive-exclude tests *.pyc
include tests/*

View File

@ -52,6 +52,13 @@ You may also install the latest source from the
pip install git+https://github.com/imacat/flask-digestauth.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 Setting the Password
==================== ====================
@ -89,7 +96,7 @@ In your ``my_app.py``:
app: flask = Flask(__name__) app: flask = Flask(__name__)
... (Configure the Flask application) ... ... (Configure the Flask application) ...
auth: DigestAuth = DigestAuth(realm="Admin") auth: DigestAuth = DigestAuth()
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -128,7 +135,6 @@ In your ``my_app/__init__.py``:
app: flask = Flask(__name__) app: flask = Flask(__name__)
... (Configure the Flask application) ... ... (Configure the Flask application) ...
auth.realm = app.config["REALM"]
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -207,7 +213,7 @@ In your ``my_app.py``:
def load_user(user_id: str) -> t.Optional[User]: def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ... ... (Load the user with the username) ...
auth: DigestAuth = DigestAuth(realm="Admin") auth: DigestAuth = DigestAuth()
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -251,7 +257,6 @@ In your ``my_app/__init__.py``:
def load_user(user_id: str) -> t.Optional[User]: def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ... ... (Load the user with the username) ...
auth.realm = app.config["REALM"]
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -340,8 +345,9 @@ A unittest Test Case
def create_app(self): def create_app(self):
app: Flask = create_app({ app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32), "SECRET_KEY": token_urlsafe(32),
"TESTING": True "DIGEST_AUTH_REALM": "admin",
}) })
app.test_client_class = Client app.test_client_class = Client
return app return app
@ -367,8 +373,9 @@ A pytest Test
@pytest.fixture() @pytest.fixture()
def app(): def app():
app: Flask = create_app({ app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32), "SECRET_KEY": token_urlsafe(32),
"TESTING": True "DIGEST_AUTH_REALM": "admin",
}) })
app.test_client_class = Client app.test_client_class = Client
yield app yield app

View File

@ -1 +0,0 @@
flask

View File

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

View File

@ -17,7 +17,7 @@ In your ``my_app.py``:
app: flask = Flask(__name__) app: flask = Flask(__name__)
... (Configure the Flask application) ... ... (Configure the Flask application) ...
auth: DigestAuth = DigestAuth(realm="Admin") auth: DigestAuth = DigestAuth()
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -58,7 +58,6 @@ In your ``my_app/__init__.py``:
app: flask = Flask(__name__) app: flask = Flask(__name__)
... (Configure the Flask application) ... ... (Configure the Flask application) ...
auth.realm = app.config["REALM"]
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -118,7 +117,7 @@ In your ``my_app.py``:
def load_user(user_id: str) -> t.Optional[User]: def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ... ... (Load the user with the username) ...
auth: DigestAuth = DigestAuth(realm="Admin") auth: DigestAuth = DigestAuth()
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -164,7 +163,6 @@ In your ``my_app/__init__.py``:
def load_user(user_id: str) -> t.Optional[User]: def load_user(user_id: str) -> t.Optional[User]:
... (Load the user with the username) ... ... (Load the user with the username) ...
auth.realm = app.config["REALM"]
auth.init_app(app) auth.init_app(app)
@auth.register_get_password @auth.register_get_password
@ -219,8 +217,9 @@ A unittest Test Case
def create_app(self): def create_app(self):
app: Flask = create_app({ app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32), "SECRET_KEY": token_urlsafe(32),
"TESTING": True "DIGEST_AUTH_REALM": "admin",
}) })
app.test_client_class = Client app.test_client_class = Client
return app return app
@ -249,8 +248,9 @@ A pytest Test
@pytest.fixture() @pytest.fixture()
def app(): def app():
app: Flask = create_app({ app: Flask = create_app({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32), "SECRET_KEY": token_urlsafe(32),
"TESTING": True "DIGEST_AUTH_REALM": "admin",
}) })
app.test_client_class = Client app.test_client_class = Client
yield app yield app

View File

@ -46,6 +46,13 @@ You may also install the latest source from the
pip install git+https://github.com/imacat/flask-digestauth.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 Setting the Password
-------------------- --------------------

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project. # The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/11/23 # 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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,6 +15,39 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
[project]
name = "Flask-DigestAuth"
version = "0.5.0"
description = "The Flask HTTP Digest Authentication project."
readme = "README.rst"
requires-python = ">=3.7"
authors = [
{name = "imacat", email = "imacat@mail.imacat.idv.tw"},
]
keywords = ["flask", "digest-authentication"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Framework :: Flask",
"Topic :: System :: Systems Administration :: Authentication/Directory",
"Intended Audience :: Developers",
]
dependencies = [
"flask",
]
[project.optional-dependencies]
test = [
"unittest",
"flask-testing",
]
[project.urls]
"Documentation" = "https://flask-digestauth.readthedocs.io"
"Repository" = "https://github.com/imacat/flask-digestauth"
"Bug Tracker" = "https://github.com/imacat/flask-digestauth/issues"
[build-system] [build-system]
requires = ["setuptools>=42"] requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"

View File

@ -1,53 +0,0 @@
# The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/11/23
# 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.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[metadata]
name = Flask-DigestAuth
version = 0.4.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-digestauth
project_urls =
Bug Tracker = https://github.com/imacat/flask-digestauth/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
Framework :: Flask
Topic :: System :: Systems Administration :: Authentication/Directory
Intended Audience :: Developers
[options]
package_dir =
= src
packages = find:
python_requires = >=3.7
install_requires =
flask
tests_require =
unittest
flask-testing
[options.packages.find]
where = src
[options.extras_require]
flask_login =
flask-login

View File

@ -46,8 +46,8 @@ class DigestAuth:
self.__serializer: URLSafeTimedSerializer \ self.__serializer: URLSafeTimedSerializer \
= URLSafeTimedSerializer(token_urlsafe(32)) = URLSafeTimedSerializer(token_urlsafe(32))
"""The serializer to generate and validate the nonce and opaque.""" """The serializer to generate and validate the nonce and opaque."""
self.realm: str = "" if realm is None else realm self.realm: str = "Login Required" if realm is None else realm
"""The realm. Default is an empty string.""" """The realm. Default is "Login Required"."""
self.algorithm: t.Optional[t.Literal["MD5", "MD5-sess"]] = 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."""
@ -336,13 +336,14 @@ class DigestAuth:
app: flask = Flask(__name__) app: flask = Flask(__name__)
auth: DigestAuth = DigestAuth() auth: DigestAuth = DigestAuth()
auth.realm = "My Admin"
auth.init_app(app) auth.init_app(app)
:param app: The Flask application. :param app: The Flask application.
:return: None. :return: None.
""" """
app.extensions["digest_auth"] = self app.extensions["digest_auth"] = self
if "DIGEST_AUTH_REALM" in app.config:
self.realm = app.config["DIGEST_AUTH_REALM"]
if hasattr(app, "login_manager"): if hasattr(app, "login_manager"):
from flask_login import LoginManager, login_user from flask_login import LoginManager, login_user
@ -381,8 +382,7 @@ class DigestAuth:
raise UnauthorizedException( raise UnauthorizedException(
"Not an HTTP digest authorization") "Not an HTTP digest authorization")
self.__authenticate(request._digest_auth_state) self.__authenticate(request._digest_auth_state)
user = login_manager.user_callback( user = login_manager.user_callback(authorization.username)
authorization.username)
login_user(user) login_user(user)
self.__on_login(user) self.__on_login(user)
return user return user

View File

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

View File

@ -1,7 +1,7 @@
# The Flask HTTP Digest Authentication Project. # The Flask HTTP Digest Authentication Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/11/23 # 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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -77,8 +77,9 @@ class FlaskLoginTestCase(TestCase):
""" """
app: Flask = Flask(__name__) app: Flask = Flask(__name__)
app.config.from_mapping({ app.config.from_mapping({
"TESTING": True,
"SECRET_KEY": token_urlsafe(32), "SECRET_KEY": token_urlsafe(32),
"TESTING": True "DIGEST_AUTH_REALM": _REALM,
}) })
app.test_client_class = Client app.test_client_class = Client
@ -92,7 +93,7 @@ class FlaskLoginTestCase(TestCase):
login_manager: flask_login.LoginManager = flask_login.LoginManager() login_manager: flask_login.LoginManager = flask_login.LoginManager()
login_manager.init_app(app) login_manager.init_app(app)
auth: DigestAuth = DigestAuth(realm=_REALM) auth: DigestAuth = DigestAuth()
auth.init_app(app) auth.init_app(app)
self.user: User = User(_USERNAME, _PASSWORD) self.user: User = User(_USERNAME, _PASSWORD)