9 Commits

5 changed files with 65 additions and 23 deletions

View File

@ -343,7 +343,7 @@ to ask the user for the username and password again.
Log In Bookkeeping
=================#
==================
You can register a callback to run when the user logs in, for ex.,
logging the log in event, adding the log in counter, etc.
@ -359,10 +359,13 @@ Writing Tests
=============
You can write tests with our test client that handles HTTP Digest
Authentication. Example for a unittest testcase:
Authentication.
Example for a unittest_ test case:
::
from flask import Flask
from flask_digest_auth import Client
from flask_testing import TestCase
from my_app import create_app
@ -385,6 +388,41 @@ Authentication. Example for a unittest testcase:
self.assertEqual(response.status_code, 200)
Example for a pytest_ test:
::
import pytest
from flask import Flask
from flask_digest_auth import Client
from my_app import create_app
@pytest.fixture()
def app():
app: Flask = create_app({
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
})
app.test_client_class = Client
yield app
@pytest.fixture()
def client(app):
return app.test_client()
def test_admin(app: Flask, client: Client):
with app.app_context():
response = self.client.get("/admin")
assert response.status_code == 401
response = self.client.get(
"/admin", digest_auth=("my_name", "my_pass"))
assert response.status_code == 200
.. _unittest: https://docs.python.org/3/library/unittest.html
.. _pytest: https://pytest.org
Copyright
=========
@ -402,6 +440,7 @@ Copyright
See the License for the specific language governing permissions and
limitations under the License.
Authors
=======

View File

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

View File

@ -110,7 +110,7 @@ class DigestAuth:
class NoLogInException(Exception):
"""The exception thrown when the user is not authorized."""
def get_logged_in_user() -> t.Optional[t.Any]:
def get_logged_in_user() -> t.Any:
"""Returns the currently logged-in user.
:return: The currently logged-in user.
@ -120,6 +120,7 @@ class DigestAuth:
raise NoLogInException
user: t.Optional[t.Any] = self.__get_user(session["user"])
if user is None:
del session["user"]
raise NoLogInException
return user

View File

@ -35,14 +35,15 @@ _PASSWORD: str = "Circle Of Life"
class User:
"""A dummy user"""
def __init__(self, username: str, password_hash: str):
def __init__(self, username: str, password: str):
"""Constructs a dummy user.
:param username: The username.
:param password_hash: The password hash.
:param password: The clear-text password.
"""
self.username: str = username
self.password_hash: str = password_hash
self.password_hash: str = make_password_hash(
_REALM, username, password)
self.visits: int = 0
@ -63,9 +64,8 @@ class AuthenticationTestCase(TestCase):
auth: DigestAuth = DigestAuth(realm=_REALM)
auth.init_app(app)
user_db: t.Dict[str, User] \
= {_USERNAME: User(
_USERNAME, make_password_hash(_REALM, _USERNAME, _PASSWORD))}
self.user: User = User(_USERNAME, _PASSWORD)
user_db: t.Dict[str, User] = {_USERNAME: self.user}
@auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]:
@ -141,7 +141,7 @@ class AuthenticationTestCase(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data.decode("UTF-8"),
f"Hello, {_USERNAME}! #2")
self.assertEqual(g.user.visits, 1)
self.assertEqual(self.user.visits, 1)
def test_stale_opaque(self) -> None:
"""Tests the stale and opaque value.
@ -218,4 +218,4 @@ class AuthenticationTestCase(TestCase):
response = self.client.get(admin_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(g.user.visits, 2)
self.assertEqual(self.user.visits, 2)

View File

@ -21,7 +21,6 @@
import typing as t
from secrets import token_urlsafe
import flask_login
from flask import Response, Flask, g, redirect, request
from flask_testing import TestCase
from werkzeug.datastructures import WWWAuthenticate, Authorization
@ -36,14 +35,15 @@ _PASSWORD: str = "Circle Of Life"
class User:
"""A dummy user."""
def __init__(self, username: str, password_hash: str):
def __init__(self, username: str, password: str):
"""Constructs a dummy user.
:param username: The username.
:param password_hash: The password hash.
:param password: The clear-text password.
"""
self.username: str = username
self.password_hash: str = password_hash
self.password_hash: str = make_password_hash(
_REALM, username, password)
self.visits: int = 0
self.is_authenticated: bool = True
self.is_active: bool = True
@ -86,9 +86,8 @@ class FlaskLoginTestCase(TestCase):
auth: DigestAuth = DigestAuth(realm=_REALM)
auth.init_app(app)
user_db: t.Dict[str, User] \
= {_USERNAME: User(
_USERNAME, make_password_hash(_REALM, _USERNAME, _PASSWORD))}
self.user: User = User(_USERNAME, _PASSWORD)
user_db: t.Dict[str, User] = {_USERNAME: self.user}
@auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]:
@ -154,7 +153,7 @@ class FlaskLoginTestCase(TestCase):
:return: None.
"""
if not self.has_flask_login:
self.skipTest("Skipped testing Flask-Login integration without it.")
self.skipTest("Skipped without Flask-Login.")
response: Response = self.client.get(self.app.url_for("admin-1"))
self.assertEqual(response.status_code, 401)
@ -167,7 +166,7 @@ class FlaskLoginTestCase(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data.decode("UTF-8"),
f"Hello, {_USERNAME}! #2")
self.assertEqual(flask_login.current_user.visits, 1)
self.assertEqual(self.user.visits, 1)
def test_stale_opaque(self) -> None:
"""Tests the stale and opaque value.
@ -175,7 +174,7 @@ class FlaskLoginTestCase(TestCase):
:return: None.
"""
if not self.has_flask_login:
self.skipTest("Skipped testing Flask-Login integration without it.")
self.skipTest("Skipped without Flask-Login.")
admin_uri: str = self.app.url_for("admin-1")
response: Response
@ -222,6 +221,9 @@ class FlaskLoginTestCase(TestCase):
:return: None.
"""
if not self.has_flask_login:
self.skipTest("Skipped without Flask-Login.")
admin_uri: str = self.app.url_for("admin-1")
logout_uri: str = self.app.url_for("logout")
response: Response
@ -253,4 +255,4 @@ class FlaskLoginTestCase(TestCase):
response = self.client.get(admin_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(flask_login.current_user.visits, 2)
self.assertEqual(self.user.visits, 2)