From e861cae2e0fba92dd986d30b9dddaff10e00d3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Wed, 26 Apr 2023 23:15:13 +0800 Subject: [PATCH] Replaced importing the "typing" module as "t" with importing the individual names in the "typing" module. We do not have as many names to import. This is also to be consistent with the practices of most major and standard packages and examples. --- src/flask_digest_auth/algo.py | 10 +++---- src/flask_digest_auth/auth.py | 52 +++++++++++++++++------------------ src/flask_digest_auth/test.py | 12 ++++---- tests/test_algo.py | 12 ++++---- tests/test_auth.py | 8 +++--- tests/test_flask_login.py | 8 +++--- 6 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/flask_digest_auth/algo.py b/src/flask_digest_auth/algo.py index 266b59c..31c05e8 100644 --- a/src/flask_digest_auth/algo.py +++ b/src/flask_digest_auth/algo.py @@ -20,8 +20,8 @@ """ from __future__ import annotations -import typing as t from hashlib import md5 +from typing import Optional, Literal def make_password_hash(realm: str, username: str, password: str) -> str: @@ -44,10 +44,10 @@ def make_password_hash(realm: str, username: str, password: str) -> str: def calc_response( method: str, uri: str, password_hash: str, - nonce: str, qop: t.Optional[t.Literal["auth", "auth-int"]] = None, - algorithm: t.Optional[t.Literal["MD5", "MD5-sess"]] = "MD5-sess", - cnonce: t.Optional[str] = None, nc: t.Optional[str] = None, - body: t.Optional[bytes] = None) -> str: + nonce: str, qop: Optional[Literal["auth", "auth-int"]] = None, + algorithm: Optional[Literal["MD5", "MD5-sess"]] = "MD5-sess", + cnonce: Optional[str] = None, nc: Optional[str] = None, + body: Optional[bytes] = None) -> str: """Calculates the response value of the HTTP digest authentication. :param method: The request method. diff --git a/src/flask_digest_auth/auth.py b/src/flask_digest_auth/auth.py index 091e897..61c0510 100644 --- a/src/flask_digest_auth/auth.py +++ b/src/flask_digest_auth/auth.py @@ -23,9 +23,9 @@ See `RFC 2617`_ HTTP Authentication: Basic and Digest Access Authentication from __future__ import annotations import sys -import typing as t from functools import wraps from secrets import token_urlsafe, randbits +from typing import Any, Optional, Literal, Callable, List from flask import g, request, Response, session, abort, Flask, Request, \ current_app @@ -38,7 +38,7 @@ from flask_digest_auth.algo import calc_response class DigestAuth: """The HTTP digest authentication.""" - def __init__(self, realm: t.Optional[str] = None): + def __init__(self, realm: Optional[str] = None): """Constructs the HTTP digest authentication. :param realm: The realm. @@ -48,16 +48,15 @@ class DigestAuth: """The serializer to generate and validate the nonce and opaque.""" 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 + self.algorithm: Optional[Literal["MD5", "MD5-sess"]] = None """The algorithm, either None, ``MD5``, or ``MD5-sess``. Default is None.""" self.use_opaque: bool = True """Whether to use an opaque. Default is True.""" - self.__domain: t.List[str] = [] + self.__domain: List[str] = [] """A list of directories that this username and password applies to. Default is empty.""" - self.__qop: t.List[t.Literal["auth", "auth-int"]] \ - = ["auth", "auth-int"] + self.__qop: List[Literal["auth", "auth-int"]] = ["auth", "auth-int"] """A list of supported quality of protection supported, either ``qop``, ``auth-int``, both, or empty. Default is both.""" self.__get_password_hash: BasePasswordHashGetter \ @@ -68,7 +67,7 @@ class DigestAuth: self.__on_login: BaseOnLogInCallback = BaseOnLogInCallback() """The callback to run when the user logs in.""" - def login_required(self, view) -> t.Callable: + def login_required(self, view) -> Callable: """The view decorator for the HTTP digest authentication. :Example: @@ -89,7 +88,7 @@ class DigestAuth: class NoLogInException(Exception): """The exception thrown when the user is not authorized.""" - def get_logged_in_user() -> t.Any: + def get_logged_in_user() -> Any: """Returns the currently logged-in user. :return: The currently logged-in user. @@ -97,13 +96,13 @@ class DigestAuth: """ if "user" not in session: raise NoLogInException - user: t.Optional[t.Any] = self.__get_user(session["user"]) + user: Optional[Any] = self.__get_user(session["user"]) if user is None: del session["user"] raise NoLogInException return user - def auth_user(state: AuthState) -> t.Any: + def auth_user(state: AuthState) -> Any: """Authenticates a user. :param state: The authentication state. @@ -121,7 +120,7 @@ class DigestAuth: return self.__get_user(authorization.username) @wraps(view) - def login_required_view(*args, **kwargs) -> t.Any: + def login_required_view(*args, **kwargs) -> Any: """The login-protected view. :param args: The positional arguments of the view. @@ -171,7 +170,7 @@ class DigestAuth: except BadData: raise UnauthorizedException("Invalid opaque") state.opaque = authorization.opaque - password_hash: t.Optional[str] \ + password_hash: Optional[str] \ = self.__get_password_hash(authorization.username) if password_hash is None: raise UnauthorizedException( @@ -202,7 +201,7 @@ class DigestAuth: :return: The ``WWW-Authenticate`` response header. """ - def get_opaque() -> t.Optional[str]: + def get_opaque() -> Optional[str]: """Returns the opaque value. :return: The opaque value. @@ -213,7 +212,7 @@ class DigestAuth: return state.opaque return self.__serializer.dumps(randbits(32), salt="opaque") - opaque: t.Optional[str] = get_opaque() + opaque: Optional[str] = get_opaque() nonce: str = self.__serializer.dumps( randbits(32), salt="nonce" if opaque is None else f"nonce-{opaque}") @@ -234,7 +233,7 @@ class DigestAuth: header += f", qop=\"{qop_list}\"" return header - def register_get_password(self, func: t.Callable[[str], t.Optional[str]])\ + def register_get_password(self, func: Callable[[str], Optional[str]]) \ -> None: """The decorator to register the callback to obtain the password hash. @@ -256,7 +255,7 @@ class DigestAuth: """The base password hash getter.""" @staticmethod - def __call__(username: str) -> t.Optional[str]: + def __call__(username: str) -> Optional[str]: """Returns the password hash of a user. :param username: The username. @@ -266,8 +265,7 @@ class DigestAuth: self.__get_password_hash = PasswordHashGetter() - def register_get_user(self, func: t.Callable[[str], t.Optional[t.Any]])\ - -> None: + def register_get_user(self, func: Callable[[str], Optional[Any]]) -> None: """The decorator to register the callback to obtain the user. :Example: @@ -287,7 +285,7 @@ class DigestAuth: """The user getter.""" @staticmethod - def __call__(username: str) -> t.Optional[t.Any]: + def __call__(username: str) -> Optional[Any]: """Returns a user. :param username: The username. @@ -297,7 +295,7 @@ class DigestAuth: self.__get_user = UserGetter() - def register_on_login(self, func: t.Callable[[t.Any], None]) -> None: + def register_on_login(self, func: Callable[[Any], None]) -> None: """The decorator to register the callback to run when the user logs in. :Example: @@ -316,7 +314,7 @@ class DigestAuth: """The callback when the user logs in.""" @staticmethod - def __call__(user: t.Any) -> None: + def __call__(user: Any) -> None: """Runs the callback when the user logs in. :param user: The logged-in user. @@ -366,7 +364,7 @@ class DigestAuth: abort(response) @login_manager.request_loader - def load_user_from_request(req: Request) -> t.Optional[t.Any]: + def load_user_from_request(req: Request) -> Optional[Any]: """Loads the user from the request header. :param req: The request. @@ -427,9 +425,9 @@ class AuthState: def __init__(self): """Constructs the authorization state.""" - self.opaque: t.Optional[str] = None + self.opaque: Optional[str] = None """The opaque value specified by the client, if valid.""" - self.stale: t.Optional[bool] = None + self.stale: Optional[bool] = None """The stale value, if there is a previous log in attempt.""" @@ -446,7 +444,7 @@ class BasePasswordHashGetter: """ @staticmethod - def __call__(username: str) -> t.Optional[str]: + def __call__(username: str) -> Optional[str]: """Returns the password hash of a user. :param username: The username. @@ -467,7 +465,7 @@ class BaseUserGetter: """ @staticmethod - def __call__(username: str) -> t.Optional[t.Any]: + def __call__(username: str) -> Optional[Any]: """Returns a user. :param username: The username. @@ -487,7 +485,7 @@ class BaseOnLogInCallback: """ @staticmethod - def __call__(user: t.Any) -> None: + def __call__(user: Any) -> None: """Runs the callback when the user logs in. :param user: The logged-in user. diff --git a/src/flask_digest_auth/test.py b/src/flask_digest_auth/test.py index bc60249..aa2b8ee 100644 --- a/src/flask_digest_auth/test.py +++ b/src/flask_digest_auth/test.py @@ -18,8 +18,8 @@ """The test client with HTTP digest authentication enabled. """ -import typing as t from secrets import token_urlsafe +from typing import Optional, Literal, Tuple, Dict from flask import g from werkzeug.datastructures import Authorization, WWWAuthenticate @@ -83,7 +83,7 @@ class Client(WerkzeugClient): .. _pytest: https://pytest.org """ - def open(self, *args, digest_auth: t.Optional[t.Tuple[str, str]] = None, + def open(self, *args, digest_auth: Optional[Tuple[str, str]] = None, **kwargs) -> TestResponse: """Opens a request. @@ -117,14 +117,14 @@ class Client(WerkzeugClient): :param password: The password. :return: The request authorization. """ - qop: t.Optional[t.Literal["auth", "auth-int"]] = None + qop: Optional[Literal["auth", "auth-int"]] = None if www_authenticate.qop is not None and "auth" in www_authenticate.qop: qop = "auth" - cnonce: t.Optional[str] = None + cnonce: Optional[str] = None if qop is not None or www_authenticate.algorithm == "MD5-sess": cnonce = token_urlsafe(8) - nc: t.Optional[str] = None + nc: Optional[str] = None count: int = 1 if qop is not None: nc: str = hex(count)[2:].zfill(8) @@ -137,7 +137,7 @@ class Client(WerkzeugClient): algorithm=www_authenticate.algorithm, cnonce=cnonce, nc=nc, body=None) - data: t.Dict[str, str] = { + data: Dict[str, str] = { "username": username, "realm": www_authenticate.realm, "nonce": www_authenticate.nonce, "uri": uri, "response": expected} if www_authenticate.algorithm is not None: diff --git a/tests/test_algo.py b/tests/test_algo.py index 78f428e..eda43fd 100644 --- a/tests/test_algo.py +++ b/tests/test_algo.py @@ -18,8 +18,8 @@ """The test case for the HTTP digest authentication algorithm. """ -import typing as t import unittest +from typing import Optional, Literal from flask_digest_auth import make_password_hash, calc_response @@ -39,11 +39,11 @@ class AlgorithmTestCase(unittest.TestCase): method: str = "GET" uri: str = "/dir/index.html" nonce: str = "dcd98b7102dd2f0e8b11d0f600bfb0c093" - qop: t.Optional[t.Literal["auth", "auth-int"]] = "auth" - algorithm: t.Optional[t.Literal["MD5", "MD5-sess"]] = None - cnonce: t.Optional[str] = "0a4f113b" - nc: t.Optional[str] = "00000001" - body: t.Optional[bytes] = None + qop: Optional[Literal["auth", "auth-int"]] = "auth" + algorithm: Optional[Literal["MD5", "MD5-sess"]] = None + cnonce: Optional[str] = "0a4f113b" + nc: Optional[str] = "00000001" + body: Optional[bytes] = None password_hash: str = make_password_hash(realm, username, password) response: str = calc_response(method, uri, password_hash, nonce, qop, diff --git a/tests/test_auth.py b/tests/test_auth.py index 02301cc..2c5efec 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -18,8 +18,8 @@ """The test case for the HTTP digest authentication. """ -import typing as t from secrets import token_urlsafe +from typing import Any, Optional, Dict from flask import Response, Flask, g, redirect, request from flask_testing import TestCase @@ -66,10 +66,10 @@ class AuthenticationTestCase(TestCase): auth: DigestAuth = DigestAuth() auth.init_app(app) self.user: User = User(_USERNAME, _PASSWORD) - user_db: t.Dict[str, User] = {_USERNAME: self.user} + user_db: Dict[str, User] = {_USERNAME: self.user} @auth.register_get_password - def get_password_hash(username: str) -> t.Optional[str]: + def get_password_hash(username: str) -> Optional[str]: """Returns the password hash of a user. :param username: The username. @@ -79,7 +79,7 @@ class AuthenticationTestCase(TestCase): else None @auth.register_get_user - def get_user(username: str) -> t.Optional[t.Any]: + def get_user(username: str) -> Optional[Any]: """Returns a user. :param username: The username. diff --git a/tests/test_flask_login.py b/tests/test_flask_login.py index e90c9b3..b636a10 100644 --- a/tests/test_flask_login.py +++ b/tests/test_flask_login.py @@ -18,8 +18,8 @@ """The test case for the Flask-Login integration. """ -import typing as t from secrets import token_urlsafe +from typing import Optional, Dict from flask import Response, Flask, g, redirect, request from flask_testing import TestCase @@ -97,10 +97,10 @@ class FlaskLoginTestCase(TestCase): auth.init_app(app) self.user: User = User(_USERNAME, _PASSWORD) - user_db: t.Dict[str, User] = {_USERNAME: self.user} + user_db: Dict[str, User] = {_USERNAME: self.user} @auth.register_get_password - def get_password_hash(username: str) -> t.Optional[str]: + def get_password_hash(username: str) -> Optional[str]: """Returns the password hash of a user. :param username: The username. @@ -119,7 +119,7 @@ class FlaskLoginTestCase(TestCase): user.visits = user.visits + 1 @login_manager.user_loader - def load_user(user_id: str) -> t.Optional[User]: + def load_user(user_id: str) -> Optional[User]: """Loads a user. :param user_id: The username.