From 190e55d76fe3ef167f399e4b784a07d2e82ddbc9 Mon Sep 17 00:00:00 2001 From: tokusumi <41147016+tokusumi@users.noreply.github.com> Date: Sun, 2 Jan 2022 20:07:18 +0900 Subject: [PATCH] Add support python3.9 (#64) --- .github/workflows/test.yml | 4 +-- fastapi_cloudauth/cognito.py | 7 +++++- fastapi_cloudauth/verification.py | 10 +++++--- pyproject.toml | 36 ++++++++++++++------------- tests/test_auth0.py | 10 +++++--- tests/test_base.py | 9 ++++--- tests/test_cognito.py | 7 ++++-- tests/test_firebase.py | 5 +++- tests/test_verification.py | 41 ++++++++++++++++++++++--------- 9 files changed, 86 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8d3bec..5d7bcb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,8 +11,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] - poetry-version: [1.0.0] + python-version: [3.6, 3.7, 3.8, 3.9] + poetry-version: [1.1.12] steps: - uses: actions/checkout@v2 diff --git a/fastapi_cloudauth/cognito.py b/fastapi_cloudauth/cognito.py index 42583e5..40653b0 100644 --- a/fastapi_cloudauth/cognito.py +++ b/fastapi_cloudauth/cognito.py @@ -61,7 +61,12 @@ class CognitoCurrentUser(UserInfoAuth): user_info = CognitoClaims def __init__( - self, region: str, userPoolId: str, client_id: str, *args: Any, **kwargs: Any, + self, + region: str, + userPoolId: str, + client_id: str, + *args: Any, + **kwargs: Any, ): url = f"https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json" jwks = JWKS(url=url) diff --git a/fastapi_cloudauth/verification.py b/fastapi_cloudauth/verification.py index fd23eed..9cd94f6 100644 --- a/fastapi_cloudauth/verification.py +++ b/fastapi_cloudauth/verification.py @@ -47,7 +47,9 @@ def __call__(self, claims: Dict[str, str], auto_error: bool = True) -> bool: class JWKS: def __init__( - self, url: str = "", fixed_keys: Optional[Dict[str, Key]] = None, + self, + url: str = "", + fixed_keys: Optional[Dict[str, Key]] = None, ): """Handle the JSON Web Key Set (JWKS), query and refresh ... Args: @@ -187,7 +189,8 @@ async def _get_publickey( if not publickey: if self.auto_error: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail=NO_PUBLICKEY, + status_code=status.HTTP_401_UNAUTHORIZED, + detail=NO_PUBLICKEY, ) else: return None @@ -344,7 +347,8 @@ def _verify_scope(self, http_auth: HTTPAuthorizationCredentials) -> bool: if not matched: if self.auto_error: raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, detail=SCOPE_NOT_MATCHED, + status_code=status.HTTP_403_FORBIDDEN, + detail=SCOPE_NOT_MATCHED, ) return False return True diff --git a/pyproject.toml b/pyproject.toml index 68e042d..1ae240c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,37 +19,39 @@ classifiers = [ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Security", "Typing :: Typed", ] [tool.poetry.dependencies] -python = "^3.6" +python = ">=3.6.2,<4.0" fastapi = ">= 0.60.1, < 1.0" python-jose = {version = ">=3.3.0,<4.0.0", extras = ["cryptography"]} -requests = "^2.24.0" +requests = ">=2.24.0,<3.0.0" [tool.poetry.dev-dependencies] -pytest = "^5.4.3" -pytest-cov = "^2.10.0" -flake8 = "^3.8.3" -mypy = "^0.782" -black = "^19.10b0" -isort = "^5.7.0" +pytest = ">=6.2.4,<7.0.0" +pytest-cov = ">=2.12.0,<4.0.0" +flake8 = ">=3.8.3,<4.0.0" +mypy = "0.910" +black = "21.9b0" +isort = ">=5.0.6,<6.0.0" uvicorn = ">=0.12.0,<0.14.0" -botocore = "^1.17.32" -boto3 = "^1.14.32" -authlib = "^0.15.2" -firebase-admin = "^4.4.0" -auth0-python = "^3.14.0" -pytest-mock = "^3.5.1" -pytest-asyncio = "^0.14.0" -autoflake = "^1.4" +botocore = ">=1.17.32" +boto3 = ">=1.14.32" +authlib = ">=0.15.2" +firebase-admin = ">=4.4.0" +auth0-python = ">=3.14.0" +pytest-mock = ">=3.5.1" +pytest-asyncio = ">=0.14.0" +autoflake = ">=1.4.0,<2.0.0" +types-requests = ">=2.26.3,<3.0.0" [tool.isort] profile = "black" known_third_party = ["fastapi", "pydantic", "starlette"] [build-system] -requires = ["poetry>=0.12"] +requires = ["poetry>=1.1.12"] build-backend = "poetry.masonry.api" diff --git a/tests/test_auth0.py b/tests/test_auth0.py index f381548..46c84f4 100644 --- a/tests/test_auth0.py +++ b/tests/test_auth0.py @@ -55,7 +55,9 @@ def init() -> Auth0sdk: """ get_token = GetToken(DOMAIN) token = get_token.client_credentials( - MGMT_CLIENTID, MGMT_CLIENT_SECRET, f"https://{DOMAIN}/api/v2/", + MGMT_CLIENTID, + MGMT_CLIENT_SECRET, + f"https://{DOMAIN}/api/v2/", ) mgmt_api_token = token["access_token"] @@ -111,7 +113,8 @@ def delete_user( def get_access_token( - username=f"test_user{info.major}{info.minor}@example.com", password="testPass1-", + username=f"test_user{info.major}{info.minor}@example.com", + password="testPass1-", ) -> Optional[str]: """ Requirements: @@ -143,7 +146,8 @@ def get_access_token( def get_id_token( - username=f"test_user{info.major}{info.minor}@example.com", password="testPass1-", + username=f"test_user{info.major}{info.minor}@example.com", + password="testPass1-", ) -> Optional[str]: """ Requirements: diff --git a/tests/test_base.py b/tests/test_base.py index 9e34baf..ee41fc6 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -78,7 +78,8 @@ def test_validation_scope(mocker, scopes): @pytest.mark.unittest @pytest.mark.asyncio @pytest.mark.parametrize( - "auth", [UserInfoAuth, ScopedAuth], + "auth", + [UserInfoAuth, ScopedAuth], ) async def test_forget_def_user_info(auth): dummy_http_auth = HTTPAuthorizationCredentials( @@ -95,7 +96,8 @@ async def test_forget_def_user_info(auth): @pytest.mark.unittest @pytest.mark.asyncio @pytest.mark.parametrize( - "auth", [UserInfoAuth, ScopedAuth], + "auth", + [UserInfoAuth, ScopedAuth], ) async def test_assign_user_info(auth): """three way to set user info schema @@ -133,7 +135,8 @@ class IatSchema(BaseModel): @pytest.mark.unittest @pytest.mark.asyncio @pytest.mark.parametrize( - "auth", [UserInfoAuth, ScopedAuth], + "auth", + [UserInfoAuth, ScopedAuth], ) async def test_extract_raw_user_info(auth): dummy_http_auth = HTTPAuthorizationCredentials( diff --git a/tests/test_cognito.py b/tests/test_cognito.py index fbac22f..ac11402 100644 --- a/tests/test_cognito.py +++ b/tests/test_cognito.py @@ -64,7 +64,9 @@ def add_test_user( except ClientError: # pragma: no cover pass # pragma: no cover client.admin_add_user_to_group( - UserPoolId=USERPOOLID, Username=username, GroupName=scope, + UserPoolId=USERPOOLID, + Username=username, + GroupName=scope, ) @@ -85,7 +87,8 @@ def get_cognito_token( def delete_cognito_user( - client, username=f"test_user{info.major}{info.minor}@example.com", + client, + username=f"test_user{info.major}{info.minor}@example.com", ): try: client.admin_delete_user(UserPoolId=USERPOOLID, Username=username) diff --git a/tests/test_firebase.py b/tests/test_firebase.py index 285f7ec..a77f4f4 100644 --- a/tests/test_firebase.py +++ b/tests/test_firebase.py @@ -49,7 +49,10 @@ def initialize(): tmpdir = tempfile.TemporaryDirectory() credentials_path = os.path.join(tmpdir.name, "sa.json") - with open(credentials_path, "w",) as f: + with open( + credentials_path, + "w", + ) as f: json.dump(credentials_json, f) cred = credentials.Certificate(credentials_path) diff --git a/tests/test_verification.py b/tests/test_verification.py index 0d5c3b4..8b8b0ef 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -26,7 +26,8 @@ @pytest.mark.asyncio async def test_malformed_token_handling(): http_auth_with_malformed_token = HTTPAuthorizationCredentials( - scheme="a", credentials="malformed-token", + scheme="a", + credentials="malformed-token", ) verifier = JWKsVerifier(jwks=JWKS.null()) @@ -89,7 +90,8 @@ def parse(t: datetime) -> datetime: async def test_refresh_jwks(mocker): too_short_exp = datetime.now() mocker.patch( - "requests.get", return_value=DummyResp(too_short_exp), + "requests.get", + return_value=DummyResp(too_short_exp), ) _jwks = DummyDecodeJWKS(url="http://") @@ -99,7 +101,8 @@ async def test_refresh_jwks(mocker): # time goes... new_exp = too_short_exp + timedelta(days=10) mocker.patch( - "requests.get", return_value=DummyResp(new_exp), + "requests.get", + return_value=DummyResp(new_exp), ) await _jwks.get_publickey("") # expired is refreshed @@ -127,14 +130,16 @@ def _set_expiration(self, resp: Response) -> Optional[datetime]: async def test_refresh_jwks_multiple(mocker): too_short_exp = datetime.now() mocker.patch( - "requests.get", return_value=DummyResp(too_short_exp), + "requests.get", + return_value=DummyResp(too_short_exp), ) _jwks = DummyDecodeCntJWKS(url="http://") # time goes... new_exp = too_short_exp + timedelta(days=10) mocker.patch( - "requests.get", return_value=DummyResp(new_exp), + "requests.get", + return_value=DummyResp(new_exp), ) # multiple expired access res = await asyncio.gather( @@ -154,7 +159,10 @@ def test_verify_scope_exeption(mocker): return_value={"dummy key": "read:test"}, ) scope_key = "dummy key" - http_auth = HTTPAuthorizationCredentials(scheme="a", credentials="dummy-token",) + http_auth = HTTPAuthorizationCredentials( + scheme="a", + credentials="dummy-token", + ) # trivial scope verifier = ScopedJWKsVerifier( @@ -184,11 +192,15 @@ def test_verify_scope_exeption(mocker): @pytest.mark.unittest @pytest.mark.parametrize( - "scopes", ["xxx:xxx yyy:yyy", ["xxx:xxx", "yyy:yyy"]], + "scopes", + ["xxx:xxx yyy:yyy", ["xxx:xxx", "yyy:yyy"]], ) def test_scope_match_all(mocker, scopes): scope_key = "dummy key" - http_auth = HTTPAuthorizationCredentials(scheme="a", credentials="dummy-token",) + http_auth = HTTPAuthorizationCredentials( + scheme="a", + credentials="dummy-token", + ) # check scope logic mocker.patch( @@ -199,7 +211,10 @@ def test_scope_match_all(mocker, scopes): # api scope < user scope verifier = ScopedJWKsVerifier( - scope_name=["xxx:xxx"], jwks=jwks, scope_key=scope_key, auto_error=False, + scope_name=["xxx:xxx"], + jwks=jwks, + scope_key=scope_key, + auto_error=False, ) assert verifier._verify_scope(http_auth) @@ -233,11 +248,15 @@ def test_scope_match_all(mocker, scopes): @pytest.mark.unittest @pytest.mark.parametrize( - "scopes", ["xxx:xxx yyy:yyy", ["xxx:xxx", "yyy:yyy"]], + "scopes", + ["xxx:xxx yyy:yyy", ["xxx:xxx", "yyy:yyy"]], ) def test_scope_match_any(mocker, scopes): scope_key = "dummy key" - http_auth = HTTPAuthorizationCredentials(scheme="a", credentials="dummy-token",) + http_auth = HTTPAuthorizationCredentials( + scheme="a", + credentials="dummy-token", + ) # check scope logic mocker.patch(