diff --git a/packages/google-auth/google/auth/crypt/rsa.py b/packages/google-auth/google/auth/crypt/rsa.py index 4b2fb39ffbda..639be9069549 100644 --- a/packages/google-auth/google/auth/crypt/rsa.py +++ b/packages/google-auth/google/auth/crypt/rsa.py @@ -24,7 +24,6 @@ from google.auth import _helpers from google.auth.crypt import _cryptography_rsa -from google.auth.crypt import _python_rsa from google.auth.crypt import base RSA_KEY_MODULE_PREFIX = "rsa.key" @@ -37,6 +36,7 @@ class RSAVerifier(base.Verifier): public_key (Union["rsa.key.PublicKey", cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey]): The public key used to verify signatures. Raises: + ImportError: if called with an rsa.key.PublicKey, when the rsa library is not installed ValueError: if an unrecognized public key is provided """ @@ -45,6 +45,8 @@ def __init__(self, public_key): if isinstance(public_key, RSAPublicKey): impl_lib = _cryptography_rsa elif module_str.startswith(RSA_KEY_MODULE_PREFIX): + from google.auth.crypt import _python_rsa + impl_lib = _python_rsa else: raise ValueError(f"unrecognized public key type: {type(public_key)}") @@ -85,6 +87,7 @@ class RSASigner(base.Signer, base.FromServiceAccountMixin): public key or certificate. Raises: + ImportError: if called with an rsa.key.PrivateKey, when the rsa library is not installed ValueError: if an unrecognized public key is provided """ @@ -93,6 +96,8 @@ def __init__(self, private_key, key_id=None): if isinstance(private_key, RSAPrivateKey): impl_lib = _cryptography_rsa elif module_str.startswith(RSA_KEY_MODULE_PREFIX): + from google.auth.crypt import _python_rsa + impl_lib = _python_rsa else: raise ValueError(f"unrecognized private key type: {type(private_key)}") diff --git a/packages/google-auth/noxfile.py b/packages/google-auth/noxfile.py index 1a649812135e..e9fc880f7476 100644 --- a/packages/google-auth/noxfile.py +++ b/packages/google-auth/noxfile.py @@ -41,7 +41,7 @@ "3.13", "3.14", ] -ALL_PYTHON = UNIT_TEST_PYTHON_VERSIONS +ALL_PYTHON = UNIT_TEST_PYTHON_VERSIONS.copy() ALL_PYTHON.extend(["3.7"]) # Error if a python version is missing @@ -115,16 +115,29 @@ def mypy(session): @nox.session(python=ALL_PYTHON) -def unit(session): +@nox.parametrize(["install_deprecated_extras"], (True, False)) +def unit(session, install_deprecated_extras): # Install all test dependencies, then install this package in-place. if session.python in ("3.7",): session.skip("Python 3.7 is no longer supported") + min_py, max_py = UNIT_TEST_PYTHON_VERSIONS[0], UNIT_TEST_PYTHON_VERSIONS[-1] + if not install_deprecated_extras and session.python not in (min_py, max_py): + # only run double tests on first and last supported versions + session.skip( + f"Extended tests only run on boundary Python versions ({min_py}, {max_py}) to reduce CI load." + ) constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) - session.install("-e", ".[testing]", "-c", constraints_path) + extras_str = "testing" + if install_deprecated_extras: + # rsa and oauth2client were both archived and support dropped, + # but we still test old code paths + session.install("oauth2client") + extras_str += ",rsa" + session.install("-e", f".[{extras_str}]", "-c", constraints_path) session.run( "pytest", f"--junitxml=unit_{session.python}_sponge_log.xml", diff --git a/packages/google-auth/setup.py b/packages/google-auth/setup.py index ba9e214b1cd3..8a66b7c39d0e 100644 --- a/packages/google-auth/setup.py +++ b/packages/google-auth/setup.py @@ -25,9 +25,6 @@ DEPENDENCIES = ( "pyasn1-modules>=0.2.1", cryptography_base_require, - # TODO: remove rsa from dependencies in next release (replaced with cryptography)i - # https://github.com/googleapis/google-auth-library-python/issues/1810 - "rsa>=3.1.4,<5", ) requests_extra_require = ["requests >= 2.20.0, < 3.0.0"] @@ -46,14 +43,14 @@ # TODO(https://github.com/googleapis/google-auth-library-python/issues/1739): Add bounds for urllib3 and packaging dependencies. urllib3_extra_require = ["urllib3", "packaging"] +rsa_extra_require = ["rsa>=3.1.4,<5"] + # Unit test requirements. testing_extra_require = [ # TODO(https://github.com/googleapis/google-auth-library-python/issues/1735): Remove `grpcio` from testing requirements once an extra is added for `grpcio` dependency. "grpcio", "flask", "freezegun", - # TODO(https://github.com/googleapis/google-auth-library-python/issues/1736): Remove `oauth2client` from testing requirements once an extra is added for `oauth2client` dependency. - "oauth2client", *pyjwt_extra_require, "pytest", "pytest-cov", @@ -86,6 +83,7 @@ "requests": requests_extra_require, "testing": testing_extra_require, "urllib3": urllib3_extra_require, + "rsa": rsa_extra_require, # TODO(https://github.com/googleapis/google-auth-library-python/issues/1735): Add an extra for `grpcio` dependency. # TODO(https://github.com/googleapis/google-auth-library-python/issues/1736): Add an extra for `oauth2client` dependency. } diff --git a/packages/google-auth/testing/constraints-3.8.txt b/packages/google-auth/testing/constraints-3.8.txt index 5eba7b60e183..3ca0e8edb179 100644 --- a/packages/google-auth/testing/constraints-3.8.txt +++ b/packages/google-auth/testing/constraints-3.8.txt @@ -7,7 +7,6 @@ # Then this file should have foo==1.14.0 pyasn1-modules==0.2.1 setuptools==40.3.0 -rsa==3.1.4 cryptography==38.0.3 aiohttp==3.6.2 requests==2.20.0 diff --git a/packages/google-auth/tests/crypt/test__python_rsa.py b/packages/google-auth/tests/crypt/test__python_rsa.py index 43539900f7fc..058c3a1abb6c 100644 --- a/packages/google-auth/tests/crypt/test__python_rsa.py +++ b/packages/google-auth/tests/crypt/test__python_rsa.py @@ -19,12 +19,13 @@ from pyasn1_modules import pem # type: ignore import pytest # type: ignore -import rsa # type: ignore from google.auth import _helpers from google.auth.crypt import _python_rsa from google.auth.crypt import base +rsa = pytest.importorskip("rsa") # type: ignore + DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") diff --git a/packages/google-auth/tests/crypt/test_rsa.py b/packages/google-auth/tests/crypt/test_rsa.py index 6f7aa2691330..4f722a6380b0 100644 --- a/packages/google-auth/tests/crypt/test_rsa.py +++ b/packages/google-auth/tests/crypt/test_rsa.py @@ -18,10 +18,15 @@ from cryptography.hazmat import backends from cryptography.hazmat.primitives import serialization import pytest -import rsa as rsa_lib + +try: + import rsa as rsa_lib + from google.auth.crypt import _python_rsa +except ImportError: + rsa_lib = None + _pyrhon_rsa = None from google.auth.crypt import _cryptography_rsa -from google.auth.crypt import _python_rsa from google.auth.crypt import rsa @@ -70,11 +75,13 @@ def test_init_with_cryptography_key(self, cryptography_public_key): assert isinstance(verifier._impl, _cryptography_rsa.RSAVerifier) assert verifier._impl._pubkey == cryptography_public_key + @pytest.mark.skipif(not rsa_lib, reason="rsa library not installed") def test_init_with_rsa_key(self, rsa_public_key): verifier = rsa.RSAVerifier(rsa_public_key) assert isinstance(verifier._impl, _python_rsa.RSAVerifier) assert verifier._impl._pubkey == rsa_public_key + @pytest.mark.skipif(not rsa_lib, reason="rsa library not installed") def test_warning_with_rsa(self, rsa_public_key): with pytest.warns(DeprecationWarning, match="The 'rsa' library is deprecated"): rsa.RSAVerifier(rsa_public_key) @@ -114,12 +121,14 @@ def test_init_with_cryptography_key(self, cryptography_private_key): assert signer._impl._key == cryptography_private_key assert signer._impl.key_id == "123" + @pytest.mark.skipif(not rsa_lib, reason="rsa library not installed") def test_init_with_rsa_key(self, rsa_private_key): signer = rsa.RSASigner(rsa_private_key, key_id="123") assert isinstance(signer._impl, _python_rsa.RSASigner) assert signer._impl._key == rsa_private_key assert signer._impl.key_id == "123" + @pytest.mark.skipif(not rsa_lib, reason="rsa library not installed") def test_warning_with_rsa(self, rsa_private_key): with pytest.warns(DeprecationWarning, match="The 'rsa' library is deprecated"): rsa.RSASigner(rsa_private_key, key_id="123") @@ -130,8 +139,8 @@ def test_init_with_unknown_key(self): with pytest.raises(ValueError): rsa.RSASigner(unknown_key) - def test_sign_delegates(self, rsa_private_key): - signer = rsa.RSASigner(rsa_private_key) + def test_sign_delegates(self, cryptography_private_key): + signer = rsa.RSASigner(cryptography_private_key) with mock.patch.object( signer._impl, "sign", return_value=b"signature" @@ -164,6 +173,7 @@ def test_end_to_end_cryptography_lib(self, private_key_bytes, public_key_bytes): assert isinstance(verifier._impl, _cryptography_rsa.RSAVerifier) assert isinstance(signer._impl, _cryptography_rsa.RSASigner) + @pytest.mark.skipif(not rsa_lib, reason="rsa library not installed") def test_end_to_end_rsa_lib(self, rsa_private_key, rsa_public_key): signer = rsa.RSASigner(rsa_private_key) message = b"Hello World"