From 704d552bbee24d13d50059b54ae64b65851e5403 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 21 Oct 2019 14:09:55 -0700 Subject: [PATCH 1/2] chore: blacken --- docs/conf.py | 125 ++++--- google/__init__.py | 2 + google/auth/__init__.py | 4 +- google/auth/_cloud_sdk.py | 42 +-- google/auth/_default.py | 64 ++-- google/auth/_helpers.py | 30 +- google/auth/_oauth2client.py | 34 +- google/auth/_service_account_info.py | 7 +- google/auth/app_engine.py | 15 +- google/auth/compute_engine/__init__.py | 5 +- google/auth/compute_engine/_metadata.py | 76 ++-- google/auth/compute_engine/credentials.py | 50 +-- google/auth/credentials.py | 18 +- google/auth/crypt/__init__.py | 7 +- google/auth/crypt/_cryptography_rsa.py | 22 +- google/auth/crypt/_python_rsa.py | 49 ++- google/auth/crypt/base.py | 22 +- google/auth/environment_vars.py | 12 +- google/auth/iam.py | 22 +- google/auth/impersonated_credentials.py | 134 ++++--- google/auth/jwt.py | 163 +++++---- google/auth/transport/__init__.py | 13 +- google/auth/transport/_http_client.py | 24 +- google/auth/transport/grpc.py | 18 +- google/auth/transport/requests.py | 66 ++-- google/auth/transport/urllib3.py | 65 ++-- google/oauth2/_client.py | 78 ++-- google/oauth2/credentials.py | 79 ++-- google/oauth2/id_token.py | 24 +- google/oauth2/service_account.py | 91 +++-- setup.py | 64 ++-- .../app_engine_test_app/appengine_config.py | 3 +- system_tests/app_engine_test_app/main.py | 30 +- system_tests/conftest.py | 33 +- system_tests/noxfile.py | 144 ++++---- system_tests/test_app_engine.py | 6 +- system_tests/test_compute_engine.py | 4 +- system_tests/test_default.py | 2 +- system_tests/test_grpc.py | 39 +- system_tests/test_oauth2_credentials.py | 22 +- system_tests/test_service_account.py | 18 +- tests/compute_engine/test__metadata.py | 117 +++--- tests/compute_engine/test_credentials.py | 346 +++++++++--------- tests/conftest.py | 8 +- tests/crypt/test__cryptography_rsa.py | 42 +-- tests/crypt/test__python_rsa.py | 64 ++-- tests/crypt/test_crypt.py | 21 +- tests/oauth2/test__client.py | 242 ++++++------ tests/oauth2/test_credentials.py | 217 ++++++----- tests/oauth2/test_id_token.py | 53 +-- tests/oauth2/test_service_account.py | 195 +++++----- tests/test__cloud_sdk.py | 93 ++--- tests/test__default.py | 258 +++++++------ tests/test__helpers.py | 93 +++-- tests/test__oauth2client.py | 99 ++--- tests/test__service_account_info.py | 21 +- tests/test_app_engine.py | 57 +-- tests/test_credentials.py | 52 +-- tests/test_iam.py | 33 +- tests/test_impersonated_credentials.py | 260 ++++++------- tests/test_jwt.py | 311 ++++++++-------- tests/transport/compliance.py | 52 +-- tests/transport/test__http_client.py | 4 +- tests/transport/test_grpc.py | 78 ++-- tests/transport/test_requests.py | 40 +- tests/transport/test_urllib3.py | 46 +-- 66 files changed, 2376 insertions(+), 2152 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2ac5f3428..831c752cf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,40 +33,40 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', - 'sphinx_docstring_typing' + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx_docstring_typing", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'google-auth' -copyright = '2016, Google, Inc.' -author = 'Google, Inc.' +project = "google-auth" +copyright = "2016, Google, Inc." +author = "Google, Inc." # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = pkg_resources.get_distribution('google-auth').version +version = pkg_resources.get_distribution("google-auth").version # The full version, including alpha/beta/rc tags. release = version @@ -89,7 +89,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -111,7 +111,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -128,21 +128,21 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = { - 'description': 'Google Auth Library for Python', - 'github_user': 'GoogleCloudPlatform', - 'github_repo': 'google-auth-library-python', - 'github_banner': True, - 'travis_button': True, - 'font_family': "'Roboto', Georgia, sans", - 'head_font_family': "'Roboto', Georgia, serif", - 'code_font_family': "'Roboto Mono', 'Consolas', monospace", + "description": "Google Auth Library for Python", + "github_user": "GoogleCloudPlatform", + "github_repo": "google-auth-library-python", + "github_banner": True, + "travis_button": True, + "font_family": "'Roboto', Georgia, sans", + "head_font_family": "'Roboto', Georgia, serif", + "code_font_family": "'Roboto Mono', 'Consolas', monospace", } # Add any paths that contain custom themes here, relative to this directory. @@ -171,7 +171,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -194,12 +194,7 @@ # html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - ] + "**": ["about.html", "navigation.html", "relations.html", "searchbox.html"] } # Additional templates that should be rendered to pages, maps page names to @@ -259,34 +254,36 @@ # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'google-authdoc' +htmlhelp_basename = "google-authdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'google-auth.tex', 'google-auth Documentation', - 'Google, Inc.', 'manual'), + ( + master_doc, + "google-auth.tex", + "google-auth Documentation", + "Google, Inc.", + "manual", + ) ] # The name of an image file (relative to this directory) to place at the top of @@ -326,10 +323,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'google-auth', 'google-auth Documentation', - [author], 1) -] +man_pages = [(master_doc, "google-auth", "google-auth Documentation", [author], 1)] # If true, show URL addresses after external links. # @@ -342,9 +336,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'google-auth', 'google-auth Documentation', - author, 'google-auth', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "google-auth", + "google-auth Documentation", + author, + "google-auth", + "One line description of project.", + "Miscellaneous", + ) ] # Documents to append as an appendix to all manuals. @@ -366,14 +366,13 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - 'python': ('https://docs.python.org/3.5', None), - 'urllib3': ('https://urllib3.readthedocs.io/en/stable', None), - 'requests': ('http://docs.python-requests.org/en/stable', None), - 'requests-oauthlib': ( - 'http://requests-oauthlib.readthedocs.io/en/stable', None), + "python": ("https://docs.python.org/3.5", None), + "urllib3": ("https://urllib3.readthedocs.io/en/stable", None), + "requests": ("http://docs.python-requests.org/en/stable", None), + "requests-oauthlib": ("http://requests-oauthlib.readthedocs.io/en/stable", None), } # Autodoc config -autoclass_content = 'both' -autodoc_member_order = 'bysource' -autodoc_mock_imports = ['grpc'] +autoclass_content = "both" +autodoc_member_order = "bysource" +autodoc_mock_imports = ["grpc"] diff --git a/google/__init__.py b/google/__init__.py index a35569c36..f36d79156 100644 --- a/google/__init__.py +++ b/google/__init__.py @@ -16,7 +16,9 @@ try: import pkg_resources + pkg_resources.declare_namespace(__name__) except ImportError: import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/google/auth/__init__.py b/google/auth/__init__.py index 65e13951c..5f78cd17e 100644 --- a/google/auth/__init__.py +++ b/google/auth/__init__.py @@ -19,9 +19,7 @@ from google.auth._default import default -__all__ = [ - 'default', -] +__all__ = ["default"] # Set default logging handler to avoid "No handler found" warnings. diff --git a/google/auth/_cloud_sdk.py b/google/auth/_cloud_sdk.py index 0d4b222f2..61ffd4f5c 100644 --- a/google/auth/_cloud_sdk.py +++ b/google/auth/_cloud_sdk.py @@ -23,20 +23,21 @@ # The ~/.config subdirectory containing gcloud credentials. -_CONFIG_DIRECTORY = 'gcloud' +_CONFIG_DIRECTORY = "gcloud" # Windows systems store config at %APPDATA%\gcloud -_WINDOWS_CONFIG_ROOT_ENV_VAR = 'APPDATA' +_WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA" # The name of the file in the Cloud SDK config that contains default # credentials. -_CREDENTIALS_FILENAME = 'application_default_credentials.json' +_CREDENTIALS_FILENAME = "application_default_credentials.json" # The name of the Cloud SDK shell script -_CLOUD_SDK_POSIX_COMMAND = 'gcloud' -_CLOUD_SDK_WINDOWS_COMMAND = 'gcloud.cmd' +_CLOUD_SDK_POSIX_COMMAND = "gcloud" +_CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd" # The command to get the Cloud SDK configuration -_CLOUD_SDK_CONFIG_COMMAND = ('config', 'config-helper', '--format', 'json') +_CLOUD_SDK_CONFIG_COMMAND = ("config", "config-helper", "--format", "json") # Cloud SDK's application-default client ID CLOUD_SDK_CLIENT_ID = ( - '764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com') + "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com" +) def get_config_path(): @@ -52,21 +53,19 @@ def get_config_path(): pass # Non-windows systems store this at ~/.config/gcloud - if os.name != 'nt': - return os.path.join( - os.path.expanduser('~'), '.config', _CONFIG_DIRECTORY) + if os.name != "nt": + return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY) # Windows systems store config at %APPDATA%\gcloud else: try: return os.path.join( - os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], - _CONFIG_DIRECTORY) + os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY + ) except KeyError: # This should never happen unless someone is really # messing with things, but we'll cover the case anyway. - drive = os.environ.get('SystemDrive', 'C:') - return os.path.join( - drive, '\\', _CONFIG_DIRECTORY) + drive = os.environ.get("SystemDrive", "C:") + return os.path.join(drive, "\\", _CONFIG_DIRECTORY) def get_application_default_credentials_path(): @@ -93,8 +92,7 @@ def load_authorized_user_credentials(info): Raises: ValueError: if the info is in the wrong format or missing data. """ - return google.oauth2.credentials.Credentials.from_authorized_user_info( - info) + return google.oauth2.credentials.Credentials.from_authorized_user_info(info) def get_project_id(): @@ -103,24 +101,24 @@ def get_project_id(): Returns: Optional[str]: The project ID. """ - if os.name == 'nt': + if os.name == "nt": command = _CLOUD_SDK_WINDOWS_COMMAND else: command = _CLOUD_SDK_POSIX_COMMAND try: output = subprocess.check_output( - (command,) + _CLOUD_SDK_CONFIG_COMMAND, - stderr=subprocess.STDOUT) + (command,) + _CLOUD_SDK_CONFIG_COMMAND, stderr=subprocess.STDOUT + ) except (subprocess.CalledProcessError, OSError, IOError): return None try: - configuration = json.loads(output.decode('utf-8')) + configuration = json.loads(output.decode("utf-8")) except ValueError: return None try: - return configuration['configuration']['properties']['core']['project'] + return configuration["configuration"]["properties"]["core"]["project"] except KeyError: return None diff --git a/google/auth/_default.py b/google/auth/_default.py index 27de58de7..32e81ba5f 100644 --- a/google/auth/_default.py +++ b/google/auth/_default.py @@ -32,8 +32,8 @@ _LOGGER = logging.getLogger(__name__) # Valid types accepted for file-based credentials. -_AUTHORIZED_USER_TYPE = 'authorized_user' -_SERVICE_ACCOUNT_TYPE = 'service_account' +_AUTHORIZED_USER_TYPE = "authorized_user" +_SERVICE_ACCOUNT_TYPE = "service_account" _VALID_TYPES = (_AUTHORIZED_USER_TYPE, _SERVICE_ACCOUNT_TYPE) # Help message when no credentials can be found. @@ -42,7 +42,9 @@ explicitly create credentials and re-run the application. For more \ information, please see \ https://cloud.google.com/docs/authentication/getting-started -""".format(env=environment_vars.CREDENTIALS).strip() +""".format( + env=environment_vars.CREDENTIALS +).strip() # Warning when using Cloud SDK user credentials _CLOUD_SDK_CREDENTIALS_WARNING = """\ @@ -62,6 +64,7 @@ def _warn_about_problematic_credentials(credentials): quota. If this is the case, warn about it. """ from google.auth import _cloud_sdk + if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID: warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING) @@ -86,20 +89,21 @@ def _load_credentials_from_file(filename): """ if not os.path.exists(filename): raise exceptions.DefaultCredentialsError( - 'File {} was not found.'.format(filename)) + "File {} was not found.".format(filename) + ) - with io.open(filename, 'r') as file_obj: + with io.open(filename, "r") as file_obj: try: info = json.load(file_obj) except ValueError as caught_exc: new_exc = exceptions.DefaultCredentialsError( - 'File {} is not a valid json file.'.format(filename), - caught_exc) + "File {} is not a valid json file.".format(filename), caught_exc + ) six.raise_from(new_exc, caught_exc) # The type key should indicate that the file is either a service account # credentials file or an authorized user credentials file. - credential_type = info.get('type') + credential_type = info.get("type") if credential_type == _AUTHORIZED_USER_TYPE: from google.auth import _cloud_sdk @@ -107,8 +111,7 @@ def _load_credentials_from_file(filename): try: credentials = _cloud_sdk.load_authorized_user_credentials(info) except ValueError as caught_exc: - msg = 'Failed to load authorized user credentials from {}'.format( - filename) + msg = "Failed to load authorized user credentials from {}".format(filename) new_exc = exceptions.DefaultCredentialsError(msg, caught_exc) six.raise_from(new_exc, caught_exc) # Authorized user credentials do not contain the project ID. @@ -119,20 +122,20 @@ def _load_credentials_from_file(filename): from google.oauth2 import service_account try: - credentials = ( - service_account.Credentials.from_service_account_info(info)) + credentials = service_account.Credentials.from_service_account_info(info) except ValueError as caught_exc: - msg = 'Failed to load service account credentials from {}'.format( - filename) + msg = "Failed to load service account credentials from {}".format(filename) new_exc = exceptions.DefaultCredentialsError(msg, caught_exc) six.raise_from(new_exc, caught_exc) - return credentials, info.get('project_id') + return credentials, info.get("project_id") else: raise exceptions.DefaultCredentialsError( - 'The file {file} does not have a valid type. ' - 'Type is {type}, expected one of {valid_types}.'.format( - file=filename, type=credential_type, valid_types=_VALID_TYPES)) + "The file {file} does not have a valid type. " + "Type is {type}, expected one of {valid_types}.".format( + file=filename, type=credential_type, valid_types=_VALID_TYPES + ) + ) def _get_gcloud_sdk_credentials(): @@ -140,14 +143,12 @@ def _get_gcloud_sdk_credentials(): from google.auth import _cloud_sdk # Check if application default credentials exist. - credentials_filename = ( - _cloud_sdk.get_application_default_credentials_path()) + credentials_filename = _cloud_sdk.get_application_default_credentials_path() if not os.path.isfile(credentials_filename): return None, None - credentials, project_id = _load_credentials_from_file( - credentials_filename) + credentials, project_id = _load_credentials_from_file(credentials_filename) if not project_id: project_id = _cloud_sdk.get_project_id() @@ -162,7 +163,8 @@ def _get_explicit_environ_credentials(): if explicit_file is not None: credentials, project_id = _load_credentials_from_file( - os.environ[environment_vars.CREDENTIALS]) + os.environ[environment_vars.CREDENTIALS] + ) return credentials, project_id @@ -292,14 +294,15 @@ def default(scopes=None, request=None): from google.auth.credentials import with_scopes_if_required explicit_project_id = os.environ.get( - environment_vars.PROJECT, - os.environ.get(environment_vars.LEGACY_PROJECT)) + environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT) + ) checkers = ( _get_explicit_environ_credentials, _get_gcloud_sdk_credentials, _get_gae_credentials, - lambda: _get_gce_credentials(request)) + lambda: _get_gce_credentials(request), + ) for checker in checkers: credentials, project_id = checker() @@ -308,10 +311,11 @@ def default(scopes=None, request=None): effective_project_id = explicit_project_id or project_id if not effective_project_id: _LOGGER.warning( - 'No project ID could be determined. Consider running ' - '`gcloud config set project` or setting the %s ' - 'environment variable', - environment_vars.PROJECT) + "No project ID could be determined. Consider running " + "`gcloud config set project` or setting the %s " + "environment variable", + environment_vars.PROJECT, + ) return credentials, effective_project_id raise exceptions.DefaultCredentialsError(_HELP_MESSAGE) diff --git a/google/auth/_helpers.py b/google/auth/_helpers.py index b32801a32..ecb88ffda 100644 --- a/google/auth/_helpers.py +++ b/google/auth/_helpers.py @@ -36,6 +36,7 @@ def copy_docstring(source_class): Callable: A decorator that will copy the docstring of the same named method in the source class to the decorated method. """ + def decorator(method): """Decorator implementation. @@ -49,12 +50,13 @@ def decorator(method): ValueError: if the method already has a docstring. """ if method.__doc__: - raise ValueError('Method already has a docstring.') + raise ValueError("Method already has a docstring.") source_method = getattr(source_class, method.__name__) method.__doc__ = source_method.__doc__ return method + return decorator @@ -79,7 +81,7 @@ def datetime_to_secs(value): return calendar.timegm(value.utctimetuple()) -def to_bytes(value, encoding='utf-8'): +def to_bytes(value, encoding="utf-8"): """Converts a string value to bytes, if necessary. Unfortunately, ``six.b`` is insufficient for this task since in @@ -97,12 +99,11 @@ def to_bytes(value, encoding='utf-8'): Raises: ValueError: If the value could not be converted to bytes. """ - result = (value.encode(encoding) - if isinstance(value, six.text_type) else value) + result = value.encode(encoding) if isinstance(value, six.text_type) else value if isinstance(result, six.binary_type): return result else: - raise ValueError('{0!r} could not be converted to bytes'.format(value)) + raise ValueError("{0!r} could not be converted to bytes".format(value)) def from_bytes(value): @@ -118,13 +119,11 @@ def from_bytes(value): Raises: ValueError: If the value could not be converted to unicode. """ - result = (value.decode('utf-8') - if isinstance(value, six.binary_type) else value) + result = value.decode("utf-8") if isinstance(value, six.binary_type) else value if isinstance(result, six.text_type): return result else: - raise ValueError( - '{0!r} could not be converted to unicode'.format(value)) + raise ValueError("{0!r} could not be converted to unicode".format(value)) def update_query(url, params, remove=None): @@ -163,9 +162,8 @@ def update_query(url, params, remove=None): query_params.update(params) # Remove any values specified in remove. query_params = { - key: value for key, value - in six.iteritems(query_params) - if key not in remove} + key: value for key, value in six.iteritems(query_params) if key not in remove + } # Re-encoded the query string. new_query = urllib.parse.urlencode(query_params, doseq=True) # Unsplit the url. @@ -183,7 +181,7 @@ def scopes_to_string(scopes): Returns: str: The scopes formatted as a single string. """ - return ' '.join(scopes) + return " ".join(scopes) def string_to_scopes(scopes): @@ -198,7 +196,7 @@ def string_to_scopes(scopes): if not scopes: return [] - return scopes.split(' ') + return scopes.split(" ") def padded_urlsafe_b64decode(value): @@ -213,7 +211,7 @@ def padded_urlsafe_b64decode(value): bytes: The decoded value """ b64string = to_bytes(value) - padded = b64string + b'=' * (-len(b64string) % 4) + padded = b64string + b"=" * (-len(b64string) % 4) return base64.urlsafe_b64decode(padded) @@ -231,4 +229,4 @@ def unpadded_urlsafe_b64encode(value): Returns: Union[str|bytes]: The encoded value """ - return base64.urlsafe_b64encode(value).rstrip(b'=') + return base64.urlsafe_b64encode(value).rstrip(b"=") diff --git a/google/auth/_oauth2client.py b/google/auth/_oauth2client.py index afe7dc45d..b14a38254 100644 --- a/google/auth/_oauth2client.py +++ b/google/auth/_oauth2client.py @@ -34,18 +34,17 @@ import oauth2client.contrib.gce import oauth2client.service_account except ImportError as caught_exc: - six.raise_from( - ImportError('oauth2client is not installed.'), caught_exc) + six.raise_from(ImportError("oauth2client is not installed."), caught_exc) try: import oauth2client.contrib.appengine # pytype: disable=import-error + _HAS_APPENGINE = True except ImportError: _HAS_APPENGINE = False -_CONVERT_ERROR_TMPL = ( - 'Unable to convert {} to a google-auth credentials class.') +_CONVERT_ERROR_TMPL = "Unable to convert {} to a google-auth credentials class." def _convert_oauth2_credentials(credentials): @@ -65,7 +64,8 @@ def _convert_oauth2_credentials(credentials): token_uri=credentials.token_uri, client_id=credentials.client_id, client_secret=credentials.client_secret, - scopes=credentials.scopes) + scopes=credentials.scopes, + ) new_credentials._expires = credentials.token_expiry @@ -85,9 +85,8 @@ def _convert_service_account_credentials(credentials): google.oauth2.service_account.Credentials: The converted credentials. """ info = credentials.serialization_data.copy() - info['token_uri'] = credentials.token_uri - return google.oauth2.service_account.Credentials.from_service_account_info( - info) + info["token_uri"] = credentials.token_uri + return google.oauth2.service_account.Credentials.from_service_account_info(info) def _convert_gce_app_assertion_credentials(credentials): @@ -101,7 +100,8 @@ def _convert_gce_app_assertion_credentials(credentials): google.oauth2.service_account.Credentials: The converted credentials. """ return google.auth.compute_engine.Credentials( - service_account_email=credentials.service_account_email) + service_account_email=credentials.service_account_email + ) def _convert_appengine_app_assertion_credentials(credentials): @@ -117,24 +117,22 @@ def _convert_appengine_app_assertion_credentials(credentials): # pylint: disable=invalid-name return google.auth.app_engine.Credentials( scopes=_helpers.string_to_scopes(credentials.scope), - service_account_id=credentials.service_account_id) + service_account_id=credentials.service_account_id, + ) _CLASS_CONVERSION_MAP = { oauth2client.client.OAuth2Credentials: _convert_oauth2_credentials, oauth2client.client.GoogleCredentials: _convert_oauth2_credentials, - oauth2client.service_account.ServiceAccountCredentials: - _convert_service_account_credentials, - oauth2client.service_account._JWTAccessCredentials: - _convert_service_account_credentials, - oauth2client.contrib.gce.AppAssertionCredentials: - _convert_gce_app_assertion_credentials, + oauth2client.service_account.ServiceAccountCredentials: _convert_service_account_credentials, + oauth2client.service_account._JWTAccessCredentials: _convert_service_account_credentials, + oauth2client.contrib.gce.AppAssertionCredentials: _convert_gce_app_assertion_credentials, } if _HAS_APPENGINE: _CLASS_CONVERSION_MAP[ - oauth2client.contrib.appengine.AppAssertionCredentials] = ( - _convert_appengine_app_assertion_credentials) + oauth2client.contrib.appengine.AppAssertionCredentials + ] = _convert_appengine_app_assertion_credentials def convert(credentials): diff --git a/google/auth/_service_account_info.py b/google/auth/_service_account_info.py index dd39ea7b2..790be92e1 100644 --- a/google/auth/_service_account_info.py +++ b/google/auth/_service_account_info.py @@ -47,8 +47,9 @@ def from_dict(data, require=None): if missing: raise ValueError( - 'Service account info was not in the expected format, missing ' - 'fields {}.'.format(', '.join(missing))) + "Service account info was not in the expected format, missing " + "fields {}.".format(", ".join(missing)) + ) # Create a signer. signer = crypt.RSASigner.from_service_account_info(data) @@ -68,6 +69,6 @@ def from_filename(filename, require=None): Tuple[ Mapping[str, str], google.auth.crypt.Signer ]: The verified info and a signer instance. """ - with io.open(filename, 'r', encoding='utf-8') as json_file: + with io.open(filename, "r", encoding="utf-8") as json_file: data = json.load(json_file) return data, from_dict(data, require=require) diff --git a/google/auth/app_engine.py b/google/auth/app_engine.py index 91ba8427d..aec86a620 100644 --- a/google/auth/app_engine.py +++ b/google/auth/app_engine.py @@ -73,13 +73,11 @@ def get_project_id(): # Pylint rightfully thinks EnvironmentError is OSError, but doesn't # realize it's a valid alias. if app_identity is None: - raise EnvironmentError( - 'The App Engine APIs are not available.') + raise EnvironmentError("The App Engine APIs are not available.") return app_identity.get_application_id() -class Credentials(credentials.Scoped, credentials.Signing, - credentials.Credentials): +class Credentials(credentials.Scoped, credentials.Signing, credentials.Credentials): """App Engine standard environment credentials. These credentials use the App Engine App Identity API to obtain access @@ -103,8 +101,7 @@ def __init__(self, scopes=None, service_account_id=None): # Pylint rightfully thinks EnvironmentError is OSError, but doesn't # realize it's a valid alias. if app_identity is None: - raise EnvironmentError( - 'The App Engine APIs are not available.') + raise EnvironmentError("The App Engine APIs are not available.") super(Credentials, self).__init__() self._scopes = scopes @@ -115,7 +112,8 @@ def __init__(self, scopes=None, service_account_id=None): def refresh(self, request): # pylint: disable=unused-argument token, ttl = app_identity.get_access_token( - self._scopes, self._service_account_id) + self._scopes, self._service_account_id + ) expiry = datetime.datetime.utcfromtimestamp(ttl) self.token, self.expiry = token, expiry @@ -139,7 +137,8 @@ def requires_scopes(self): @_helpers.copy_docstring(credentials.Scoped) def with_scopes(self, scopes): return self.__class__( - scopes=scopes, service_account_id=self._service_account_id) + scopes=scopes, service_account_id=self._service_account_id + ) @_helpers.copy_docstring(credentials.Signing) def sign_bytes(self, message): diff --git a/google/auth/compute_engine/__init__.py b/google/auth/compute_engine/__init__.py index ca31b4643..461f104dd 100644 --- a/google/auth/compute_engine/__init__.py +++ b/google/auth/compute_engine/__init__.py @@ -18,7 +18,4 @@ from google.auth.compute_engine.credentials import IDTokenCredentials -__all__ = [ - 'Credentials', - 'IDTokenCredentials', -] +__all__ = ["Credentials", "IDTokenCredentials"] diff --git a/google/auth/compute_engine/_metadata.py b/google/auth/compute_engine/_metadata.py index d8004bb24..f4fae7298 100644 --- a/google/auth/compute_engine/_metadata.py +++ b/google/auth/compute_engine/_metadata.py @@ -32,21 +32,23 @@ _LOGGER = logging.getLogger(__name__) -_METADATA_ROOT = 'http://{}/computeMetadata/v1/'.format( - os.getenv(environment_vars.GCE_METADATA_ROOT, 'metadata.google.internal')) +_METADATA_ROOT = "http://{}/computeMetadata/v1/".format( + os.getenv(environment_vars.GCE_METADATA_ROOT, "metadata.google.internal") +) # This is used to ping the metadata server, it avoids the cost of a DNS # lookup. -_METADATA_IP_ROOT = 'http://{}'.format( - os.getenv(environment_vars.GCE_METADATA_IP, '169.254.169.254')) -_METADATA_FLAVOR_HEADER = 'metadata-flavor' -_METADATA_FLAVOR_VALUE = 'Google' +_METADATA_IP_ROOT = "http://{}".format( + os.getenv(environment_vars.GCE_METADATA_IP, "169.254.169.254") +) +_METADATA_FLAVOR_HEADER = "metadata-flavor" +_METADATA_FLAVOR_VALUE = "Google" _METADATA_HEADERS = {_METADATA_FLAVOR_HEADER: _METADATA_FLAVOR_VALUE} # Timeout in seconds to wait for the GCE metadata server when detecting the # GCE environment. try: - _METADATA_DEFAULT_TIMEOUT = int(os.getenv('GCE_METADATA_TIMEOUT', 3)) + _METADATA_DEFAULT_TIMEOUT = int(os.getenv("GCE_METADATA_TIMEOUT", 3)) except ValueError: # pragma: NO COVER _METADATA_DEFAULT_TIMEOUT = 3 @@ -74,16 +76,24 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3): while retries < retry_count: try: response = request( - url=_METADATA_IP_ROOT, method='GET', headers=_METADATA_HEADERS, - timeout=timeout) + url=_METADATA_IP_ROOT, + method="GET", + headers=_METADATA_HEADERS, + timeout=timeout, + ) metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER) - return (response.status == http_client.OK and - metadata_flavor == _METADATA_FLAVOR_VALUE) + return ( + response.status == http_client.OK + and metadata_flavor == _METADATA_FLAVOR_VALUE + ) except exceptions.TransportError: - _LOGGER.info('Compute Engine Metadata server unavailable on' - 'attempt %s of %s', retries+1, retry_count) + _LOGGER.info( + "Compute Engine Metadata server unavailable on" "attempt %s of %s", + retries + 1, + retry_count, + ) retries += 1 return False @@ -115,29 +125,33 @@ def get(request, path, root=_METADATA_ROOT, recursive=False): query_params = {} if recursive: - query_params['recursive'] = 'true' + query_params["recursive"] = "true" url = _helpers.update_query(base_url, query_params) - response = request(url=url, method='GET', headers=_METADATA_HEADERS) + response = request(url=url, method="GET", headers=_METADATA_HEADERS) if response.status == http_client.OK: content = _helpers.from_bytes(response.data) - if response.headers['content-type'] == 'application/json': + if response.headers["content-type"] == "application/json": try: return json.loads(content) except ValueError as caught_exc: new_exc = exceptions.TransportError( - 'Received invalid JSON from the Google Compute Engine' - 'metadata service: {:.20}'.format(content)) + "Received invalid JSON from the Google Compute Engine" + "metadata service: {:.20}".format(content) + ) six.raise_from(new_exc, caught_exc) else: return content else: raise exceptions.TransportError( - 'Failed to retrieve {} from the Google Compute Engine' - 'metadata service. Status: {} Response:\n{}'.format( - url, response.status, response.data), response) + "Failed to retrieve {} from the Google Compute Engine" + "metadata service. Status: {} Response:\n{}".format( + url, response.status, response.data + ), + response, + ) def get_project_id(request): @@ -154,10 +168,10 @@ def get_project_id(request): google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ - return get(request, 'project/project-id') + return get(request, "project/project-id") -def get_service_account_info(request, service_account='default'): +def get_service_account_info(request, service_account="default"): """Get information about a service account from the metadata server. Args: @@ -182,11 +196,12 @@ def get_service_account_info(request, service_account='default'): """ return get( request, - 'instance/service-accounts/{0}/'.format(service_account), - recursive=True) + "instance/service-accounts/{0}/".format(service_account), + recursive=True, + ) -def get_service_account_token(request, service_account='default'): +def get_service_account_token(request, service_account="default"): """Get the OAuth 2.0 access token for a service account. Args: @@ -204,8 +219,9 @@ def get_service_account_token(request, service_account='default'): retrieving metadata. """ token_json = get( - request, - 'instance/service-accounts/{0}/token'.format(service_account)) + request, "instance/service-accounts/{0}/token".format(service_account) + ) token_expiry = _helpers.utcnow() + datetime.timedelta( - seconds=token_json['expires_in']) - return token_json['access_token'], token_expiry + seconds=token_json["expires_in"] + ) + return token_json["access_token"], token_expiry diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py index d9c6e26d6..eeb92f515 100644 --- a/google/auth/compute_engine/credentials.py +++ b/google/auth/compute_engine/credentials.py @@ -54,7 +54,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): https://cloud.google.com/compute/docs/authentication#using """ - def __init__(self, service_account_email='default'): + def __init__(self, service_account_email="default"): """ Args: service_account_email (str): The service account email to use, or @@ -74,11 +74,11 @@ def _retrieve_info(self, request): HTTP requests. """ info = _metadata.get_service_account_info( - request, - service_account=self._service_account_email) + request, service_account=self._service_account_email + ) - self._service_account_email = info['email'] - self._scopes = info['scopes'] + self._service_account_email = info["email"] + self._scopes = info["scopes"] def refresh(self, request): """Refresh the access token and scopes. @@ -95,8 +95,8 @@ def refresh(self, request): try: self._retrieve_info(request) self.token, self.expiry = _metadata.get_service_account_token( - request, - service_account=self._service_account_email) + request, service_account=self._service_account_email + ) except exceptions.TransportError as caught_exc: new_exc = exceptions.RefreshError(caught_exc) six.raise_from(new_exc, caught_exc) @@ -117,7 +117,7 @@ def requires_scopes(self): _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds -_DEFAULT_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token' +_DEFAULT_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token" class IDTokenCredentials(credentials.Credentials, credentials.Signing): @@ -128,10 +128,15 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing): In order for this to work, the GCE instance must have been started with a service account that has access to the IAM Cloud API. """ - def __init__(self, request, target_audience, - token_uri=_DEFAULT_TOKEN_URI, - additional_claims=None, - service_account_email=None): + + def __init__( + self, + request, + target_audience, + token_uri=_DEFAULT_TOKEN_URI, + additional_claims=None, + service_account_email=None, + ): """ Args: request (google.auth.transport.Request): The object used to make @@ -150,13 +155,14 @@ def __init__(self, request, target_audience, if service_account_email is None: sa_info = _metadata.get_service_account_info(request) - service_account_email = sa_info['email'] + service_account_email = sa_info["email"] self._service_account_email = service_account_email self._signer = iam.Signer( request=request, credentials=Credentials(), - service_account_email=service_account_email) + service_account_email=service_account_email, + ) self._token_uri = token_uri self._target_audience = target_audience @@ -181,7 +187,8 @@ def with_target_audience(self, target_audience): service_account_email=self._service_account_email, token_uri=self._token_uri, target_audience=target_audience, - additional_claims=self._additional_claims.copy()) + additional_claims=self._additional_claims.copy(), + ) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. @@ -195,15 +202,15 @@ def _make_authorization_grant_assertion(self): expiry = now + lifetime payload = { - 'iat': _helpers.datetime_to_secs(now), - 'exp': _helpers.datetime_to_secs(expiry), + "iat": _helpers.datetime_to_secs(now), + "exp": _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. - 'iss': self.service_account_email, + "iss": self.service_account_email, # The audience must be the auth token endpoint's URI - 'aud': self._token_uri, + "aud": self._token_uri, # The target audience specifies which service the ID token is # intended for. - 'target_audience': self._target_audience + "target_audience": self._target_audience, } payload.update(self._additional_claims) @@ -216,7 +223,8 @@ def _make_authorization_grant_assertion(self): def refresh(self, request): assertion = self._make_authorization_grant_assertion() access_token, expiry, _ = _client.id_token_jwt_grant( - request, self._token_uri, assertion) + request, self._token_uri, assertion + ) self.token = access_token self.expiry = expiry diff --git a/google/auth/credentials.py b/google/auth/credentials.py index 8ff1f0251..81bbd0316 100644 --- a/google/auth/credentials.py +++ b/google/auth/credentials.py @@ -41,6 +41,7 @@ class Credentials(object): construction. Some classes will provide mechanisms to copy the credentials with modifications such as :meth:`ScopedCredentials.with_scopes`. """ + def __init__(self): self.token = None """str: The bearer token that can be used in HTTP headers to make @@ -88,7 +89,7 @@ def refresh(self, request): """ # pylint: disable=missing-raises-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Refresh must be implemented') + raise NotImplementedError("Refresh must be implemented") def apply(self, headers, token=None): """Apply the token to the authentication header. @@ -98,8 +99,9 @@ def apply(self, headers, token=None): token (Optional[str]): If specified, overrides the current access token. """ - headers['authorization'] = 'Bearer {}'.format( - _helpers.from_bytes(token or self.token)) + headers["authorization"] = "Bearer {}".format( + _helpers.from_bytes(token or self.token) + ) def before_request(self, request, method, url, headers): """Performs credential-specific before request logic. @@ -189,6 +191,7 @@ class ReadOnlyScoped(object): .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 """ + def __init__(self): super(ReadOnlyScoped, self).__init__() self._scopes = None @@ -247,6 +250,7 @@ class Scoped(ReadOnlyScoped): .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3 """ + @abc.abstractmethod def with_scopes(self, scopes): """Create a copy of these credentials with the specified scopes. @@ -260,7 +264,7 @@ def with_scopes(self, scopes): This can be avoided by checking :attr:`requires_scopes` before calling this method. """ - raise NotImplementedError('This class does not require scoping.') + raise NotImplementedError("This class does not require scoping.") def with_scopes_if_required(credentials, scopes): @@ -305,18 +309,18 @@ def sign_bytes(self, message): """ # pylint: disable=missing-raises-doc,redundant-returns-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Sign bytes must be implemented.') + raise NotImplementedError("Sign bytes must be implemented.") @abc.abstractproperty def signer_email(self): """Optional[str]: An email address that identifies the signer.""" # pylint: disable=missing-raises-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Signer email must be implemented.') + raise NotImplementedError("Signer email must be implemented.") @abc.abstractproperty def signer(self): """google.auth.crypt.Signer: The signer used to sign bytes.""" # pylint: disable=missing-raises-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Signer must be implemented.') + raise NotImplementedError("Signer must be implemented.") diff --git a/google/auth/crypt/__init__.py b/google/auth/crypt/__init__.py index 7baa206ec..bf66e7100 100644 --- a/google/auth/crypt/__init__.py +++ b/google/auth/crypt/__init__.py @@ -39,12 +39,7 @@ from google.auth.crypt import rsa -__all__ = [ - 'RSASigner', - 'RSAVerifier', - 'Signer', - 'Verifier', -] +__all__ = ["RSASigner", "RSAVerifier", "Signer", "Verifier"] # Aliases to maintain the v1.0.0 interface, as the crypt module was split # into submodules. diff --git a/google/auth/crypt/_cryptography_rsa.py b/google/auth/crypt/_cryptography_rsa.py index 87076b0ab..285cf5ccf 100644 --- a/google/auth/crypt/_cryptography_rsa.py +++ b/google/auth/crypt/_cryptography_rsa.py @@ -31,18 +31,18 @@ from google.auth.crypt import base _IMPORT_ERROR_MSG = ( - 'cryptography>=1.4.0 is required to use cryptography-based RSA ' - 'implementation.') + "cryptography>=1.4.0 is required to use cryptography-based RSA " "implementation." +) try: # pragma: NO COVER - release = pkg_resources.get_distribution('cryptography').parsed_version - if release < pkg_resources.parse_version('1.4.0'): + release = pkg_resources.get_distribution("cryptography").parsed_version + if release < pkg_resources.parse_version("1.4.0"): raise ImportError(_IMPORT_ERROR_MSG) except pkg_resources.DistributionNotFound: # pragma: NO COVER raise ImportError(_IMPORT_ERROR_MSG) -_CERTIFICATE_MARKER = b'-----BEGIN CERTIFICATE-----' +_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----" _BACKEND = backends.default_backend() _PADDING = padding.PKCS1v15() _SHA256 = hashes.SHA256() @@ -88,12 +88,12 @@ def from_string(cls, public_key): if _CERTIFICATE_MARKER in public_key_data: cert = cryptography.x509.load_pem_x509_certificate( - public_key_data, _BACKEND) + public_key_data, _BACKEND + ) pubkey = cert.public_key() else: - pubkey = serialization.load_pem_public_key( - public_key_data, _BACKEND) + pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND) return cls(pubkey) @@ -122,8 +122,7 @@ def key_id(self): @_helpers.copy_docstring(base.Signer) def sign(self, message): message = _helpers.to_bytes(message) - return self._key.sign( - message, _PADDING, _SHA256) + return self._key.sign(message, _PADDING, _SHA256) @classmethod def from_string(cls, key, key_id=None): @@ -145,5 +144,6 @@ def from_string(cls, key, key_id=None): """ key = _helpers.to_bytes(key) private_key = serialization.load_pem_private_key( - key, password=None, backend=_BACKEND) + key, password=None, backend=_BACKEND + ) return cls(private_key, key_id=key_id) diff --git a/google/auth/crypt/_python_rsa.py b/google/auth/crypt/_python_rsa.py index 44aa79191..d53991c38 100644 --- a/google/auth/crypt/_python_rsa.py +++ b/google/auth/crypt/_python_rsa.py @@ -32,11 +32,9 @@ from google.auth.crypt import base _POW2 = (128, 64, 32, 16, 8, 4, 2, 1) -_CERTIFICATE_MARKER = b'-----BEGIN CERTIFICATE-----' -_PKCS1_MARKER = ('-----BEGIN RSA PRIVATE KEY-----', - '-----END RSA PRIVATE KEY-----') -_PKCS8_MARKER = ('-----BEGIN PRIVATE KEY-----', - '-----END PRIVATE KEY-----') +_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----" +_PKCS1_MARKER = ("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----") +_PKCS8_MARKER = ("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----") _PKCS8_SPEC = PrivateKeyInfo() @@ -55,9 +53,8 @@ def _bit_list_to_bytes(bit_list): num_bits = len(bit_list) byte_vals = bytearray() for start in six.moves.xrange(0, num_bits, 8): - curr_bits = bit_list[start:start + 8] - char_val = sum( - val * digit for val, digit in six.moves.zip(_POW2, curr_bits)) + curr_bits = bit_list[start : start + 8] + char_val = sum(val * digit for val, digit in six.moves.zip(_POW2, curr_bits)) byte_vals.append(char_val) return bytes(byte_vals) @@ -101,16 +98,16 @@ def from_string(cls, public_key): # If this is a certificate, extract the public key info. if is_x509_cert: - der = rsa.pem.load_pem(public_key, 'CERTIFICATE') + der = rsa.pem.load_pem(public_key, "CERTIFICATE") asn1_cert, remaining = decoder.decode(der, asn1Spec=Certificate()) - if remaining != b'': - raise ValueError('Unused bytes', remaining) + if remaining != b"": + raise ValueError("Unused bytes", remaining) - cert_info = asn1_cert['tbsCertificate']['subjectPublicKeyInfo'] - key_bytes = _bit_list_to_bytes(cert_info['subjectPublicKey']) - pubkey = rsa.PublicKey.load_pkcs1(key_bytes, 'DER') + cert_info = asn1_cert["tbsCertificate"]["subjectPublicKeyInfo"] + key_bytes = _bit_list_to_bytes(cert_info["subjectPublicKey"]) + pubkey = rsa.PublicKey.load_pkcs1(key_bytes, "DER") else: - pubkey = rsa.PublicKey.load_pkcs1(public_key, 'PEM') + pubkey = rsa.PublicKey.load_pkcs1(public_key, "PEM") return cls(pubkey) @@ -136,7 +133,7 @@ def key_id(self): @_helpers.copy_docstring(base.Signer) def sign(self, message): message = _helpers.to_bytes(message) - return rsa.pkcs1.sign(message, self._key, 'SHA-256') + return rsa.pkcs1.sign(message, self._key, "SHA-256") @classmethod def from_string(cls, key, key_id=None): @@ -155,22 +152,22 @@ def from_string(cls, key, key_id=None): """ key = _helpers.from_bytes(key) # PEM expects str in Python 3 marker_id, key_bytes = pem.readPemBlocksFromFile( - six.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER) + six.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER + ) # Key is in pkcs1 format. if marker_id == 0: - private_key = rsa.key.PrivateKey.load_pkcs1( - key_bytes, format='DER') + private_key = rsa.key.PrivateKey.load_pkcs1(key_bytes, format="DER") # Key is in pkcs8. elif marker_id == 1: - key_info, remaining = decoder.decode( - key_bytes, asn1Spec=_PKCS8_SPEC) - if remaining != b'': - raise ValueError('Unused bytes', remaining) - private_key_info = key_info.getComponentByName('privateKey') + key_info, remaining = decoder.decode(key_bytes, asn1Spec=_PKCS8_SPEC) + if remaining != b"": + raise ValueError("Unused bytes", remaining) + private_key_info = key_info.getComponentByName("privateKey") private_key = rsa.key.PrivateKey.load_pkcs1( - private_key_info.asOctets(), format='DER') + private_key_info.asOctets(), format="DER" + ) else: - raise ValueError('No key could be detected.') + raise ValueError("No key could be detected.") return cls(private_key, key_id=key_id) diff --git a/google/auth/crypt/base.py b/google/auth/crypt/base.py index c6c042721..ceb6e9c06 100644 --- a/google/auth/crypt/base.py +++ b/google/auth/crypt/base.py @@ -21,8 +21,8 @@ import six -_JSON_FILE_PRIVATE_KEY = 'private_key' -_JSON_FILE_PRIVATE_KEY_ID = 'private_key_id' +_JSON_FILE_PRIVATE_KEY = "private_key" +_JSON_FILE_PRIVATE_KEY_ID = "private_key_id" @six.add_metaclass(abc.ABCMeta) @@ -43,7 +43,7 @@ def verify(self, message, signature): """ # pylint: disable=missing-raises-doc,redundant-returns-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Verify must be implemented') + raise NotImplementedError("Verify must be implemented") @six.add_metaclass(abc.ABCMeta) @@ -53,7 +53,7 @@ class Signer(object): @abc.abstractproperty def key_id(self): """Optional[str]: The key ID used to identify this private key.""" - raise NotImplementedError('Key id must be implemented') + raise NotImplementedError("Key id must be implemented") @abc.abstractmethod def sign(self, message): @@ -67,7 +67,7 @@ def sign(self, message): """ # pylint: disable=missing-raises-doc,redundant-returns-doc # (pylint doesn't recognize that this is abstract) - raise NotImplementedError('Sign must be implemented') + raise NotImplementedError("Sign must be implemented") @six.add_metaclass(abc.ABCMeta) @@ -88,7 +88,7 @@ def from_string(cls, key, key_id=None): Raises: ValueError: If the key cannot be parsed. """ - raise NotImplementedError('from_string must be implemented') + raise NotImplementedError("from_string must be implemented") @classmethod def from_service_account_info(cls, info): @@ -107,12 +107,12 @@ def from_service_account_info(cls, info): """ if _JSON_FILE_PRIVATE_KEY not in info: raise ValueError( - 'The private_key field was not found in the service account ' - 'info.') + "The private_key field was not found in the service account " "info." + ) return cls.from_string( - info[_JSON_FILE_PRIVATE_KEY], - info.get(_JSON_FILE_PRIVATE_KEY_ID)) + info[_JSON_FILE_PRIVATE_KEY], info.get(_JSON_FILE_PRIVATE_KEY_ID) + ) @classmethod def from_service_account_file(cls, filename): @@ -125,7 +125,7 @@ def from_service_account_file(cls, filename): Returns: google.auth.crypt.Signer: The constructed signer. """ - with io.open(filename, 'r', encoding='utf-8') as json_file: + with io.open(filename, "r", encoding="utf-8") as json_file: data = json.load(json_file) return cls.from_service_account_info(data) diff --git a/google/auth/environment_vars.py b/google/auth/environment_vars.py index 0110e6a3c..96d2ffc28 100644 --- a/google/auth/environment_vars.py +++ b/google/auth/environment_vars.py @@ -15,35 +15,35 @@ """Environment variables used by :mod:`google.auth`.""" -PROJECT = 'GOOGLE_CLOUD_PROJECT' +PROJECT = "GOOGLE_CLOUD_PROJECT" """Environment variable defining default project. This used by :func:`google.auth.default` to explicitly set a project ID. This environment variable is also used by the Google Cloud Python Library. """ -LEGACY_PROJECT = 'GCLOUD_PROJECT' +LEGACY_PROJECT = "GCLOUD_PROJECT" """Previously used environment variable defining the default project. This environment variable is used instead of the current one in some situations (such as Google App Engine). """ -CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS' +CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS" """Environment variable defining the location of Google application default credentials.""" # The environment variable name which can replace ~/.config if set. -CLOUD_SDK_CONFIG_DIR = 'CLOUDSDK_CONFIG' +CLOUD_SDK_CONFIG_DIR = "CLOUDSDK_CONFIG" """Environment variable defines the location of Google Cloud SDK's config files.""" # These two variables allow for customization of the addresses used when # contacting the GCE metadata service. -GCE_METADATA_ROOT = 'GCE_METADATA_ROOT' +GCE_METADATA_ROOT = "GCE_METADATA_ROOT" """Environment variable providing an alternate hostname or host:port to be used for GCE metadata requests.""" -GCE_METADATA_IP = 'GCE_METADATA_IP' +GCE_METADATA_IP = "GCE_METADATA_IP" """Environment variable providing an alternate ip:port to be used for ip-only GCE metadata requests.""" diff --git a/google/auth/iam.py b/google/auth/iam.py index e091e47f3..a43872658 100644 --- a/google/auth/iam.py +++ b/google/auth/iam.py @@ -28,9 +28,8 @@ from google.auth import crypt from google.auth import exceptions -_IAM_API_ROOT_URI = 'https://iam.googleapis.com/v1' -_SIGN_BLOB_URI = ( - _IAM_API_ROOT_URI + '/projects/-/serviceAccounts/{}:signBlob?alt=json') +_IAM_API_ROOT_URI = "https://iam.googleapis.com/v1" +_SIGN_BLOB_URI = _IAM_API_ROOT_URI + "/projects/-/serviceAccounts/{}:signBlob?alt=json" class Signer(crypt.Signer): @@ -68,23 +67,20 @@ def _make_signing_request(self, message): """Makes a request to the API signBlob API.""" message = _helpers.to_bytes(message) - method = 'POST' + method = "POST" url = _SIGN_BLOB_URI.format(self._service_account_email) headers = {} - body = json.dumps({ - 'bytesToSign': base64.b64encode(message).decode('utf-8'), - }) + body = json.dumps({"bytesToSign": base64.b64encode(message).decode("utf-8")}) self._credentials.before_request(self._request, method, url, headers) - response = self._request( - url=url, method=method, body=body, headers=headers) + response = self._request(url=url, method=method, body=body, headers=headers) if response.status != http_client.OK: raise exceptions.TransportError( - 'Error calling the IAM signBytes API: {}'.format( - response.data)) + "Error calling the IAM signBytes API: {}".format(response.data) + ) - return json.loads(response.data.decode('utf-8')) + return json.loads(response.data.decode("utf-8")) @property def key_id(self): @@ -99,4 +95,4 @@ def key_id(self): @_helpers.copy_docstring(crypt.Signer) def sign(self, message): response = self._make_signing_request(message) - return base64.b64decode(response['signature']) + return base64.b64decode(response["signature"]) diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py index bb2bbf26a..70fa5dc9c 100644 --- a/google/auth/impersonated_credentials.py +++ b/google/auth/impersonated_credentials.py @@ -41,22 +41,28 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds -_IAM_SCOPE = ['https://www.googleapis.com/auth/iam'] +_IAM_SCOPE = ["https://www.googleapis.com/auth/iam"] -_IAM_ENDPOINT = ('https://iamcredentials.googleapis.com/v1/projects/-' + - '/serviceAccounts/{}:generateAccessToken') +_IAM_ENDPOINT = ( + "https://iamcredentials.googleapis.com/v1/projects/-" + + "/serviceAccounts/{}:generateAccessToken" +) -_IAM_SIGN_ENDPOINT = ('https://iamcredentials.googleapis.com/v1/projects/-' + - '/serviceAccounts/{}:signBlob') +_IAM_SIGN_ENDPOINT = ( + "https://iamcredentials.googleapis.com/v1/projects/-" + + "/serviceAccounts/{}:signBlob" +) -_IAM_IDTOKEN_ENDPOINT = ('https://iamcredentials.googleapis.com/v1/' + - 'projects/-/serviceAccounts/{}:generateIdToken') +_IAM_IDTOKEN_ENDPOINT = ( + "https://iamcredentials.googleapis.com/v1/" + + "projects/-/serviceAccounts/{}:generateIdToken" +) -_REFRESH_ERROR = 'Unable to acquire impersonated credentials' +_REFRESH_ERROR = "Unable to acquire impersonated credentials" _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds -_DEFAULT_TOKEN_URI = 'https://oauth2.googleapis.com/token' +_DEFAULT_TOKEN_URI = "https://oauth2.googleapis.com/token" def _make_iam_token_request(request, principal, headers, body): @@ -80,34 +86,31 @@ def _make_iam_token_request(request, principal, headers, body): body = json.dumps(body) - response = request( - url=iam_endpoint, - method='POST', - headers=headers, - body=body) + response = request(url=iam_endpoint, method="POST", headers=headers, body=body) - response_body = response.data.decode('utf-8') + response_body = response.data.decode("utf-8") if response.status != http_client.OK: exceptions.RefreshError(_REFRESH_ERROR, response_body) try: - token_response = json.loads(response.data.decode('utf-8')) - token = token_response['accessToken'] - expiry = datetime.strptime( - token_response['expireTime'], '%Y-%m-%dT%H:%M:%SZ') + token_response = json.loads(response.data.decode("utf-8")) + token = token_response["accessToken"] + expiry = datetime.strptime(token_response["expireTime"], "%Y-%m-%dT%H:%M:%SZ") return token, expiry except (KeyError, ValueError) as caught_exc: new_exc = exceptions.RefreshError( - '{}: No access token or invalid expiration in response.'.format( - _REFRESH_ERROR), - response_body) + "{}: No access token or invalid expiration in response.".format( + _REFRESH_ERROR + ), + response_body, + ) six.raise_from(new_exc, caught_exc) -class Credentials(credentials.Credentials, credentials.Signing): +class Credentials(credentials.Credentials, credentials.Signing): """This module defines impersonated credentials which are essentially impersonated identities. @@ -169,9 +172,14 @@ class Credentials(credentials.Credentials, credentials.Signing): print(bucket.name) """ - def __init__(self, source_credentials, target_principal, - target_scopes, delegates=None, - lifetime=_DEFAULT_TOKEN_LIFETIME_SECS): + def __init__( + self, + source_credentials, + target_principal, + target_scopes, + delegates=None, + lifetime=_DEFAULT_TOKEN_LIFETIME_SECS, + ): """ Args: source_credentials (google.auth.Credentials): The source credential @@ -228,12 +236,10 @@ def _update_token(self, request): body = { "delegates": self._delegates, "scope": self._target_scopes, - "lifetime": str(self._lifetime) + "s" + "lifetime": str(self._lifetime) + "s", } - headers = { - 'Content-Type': 'application/json', - } + headers = {"Content-Type": "application/json"} # Apply the source credentials authentication info. self._source_credentials.apply(headers) @@ -242,29 +248,24 @@ def _update_token(self, request): request=request, principal=self._target_principal, headers=headers, - body=body) + body=body, + ) def sign_bytes(self, message): iam_sign_endpoint = _IAM_SIGN_ENDPOINT.format(self._target_principal) - body = { - "payload": base64.b64encode(message), - "delegates": self._delegates - } + body = {"payload": base64.b64encode(message), "delegates": self._delegates} - headers = { - 'Content-Type': 'application/json', - } + headers = {"Content-Type": "application/json"} authed_session = AuthorizedSession(self._source_credentials) response = authed_session.post( - url=iam_sign_endpoint, - headers=headers, - json=body) + url=iam_sign_endpoint, headers=headers, json=body + ) - return base64.b64decode(response.json()['signedBlob']) + return base64.b64decode(response.json()["signedBlob"]) @property def signer_email(self): @@ -283,8 +284,8 @@ class IDTokenCredentials(credentials.Credentials): """Open ID Connect ID Token-based service account credentials. """ - def __init__(self, target_credentials, - target_audience=None, include_email=False): + + def __init__(self, target_credentials, target_audience=None, include_email=False): """ Args: target_credentials (google.auth.Credentials): The target @@ -294,57 +295,54 @@ def __init__(self, target_credentials, """ super(IDTokenCredentials, self).__init__() - if not isinstance(target_credentials, - Credentials): - raise exceptions.GoogleAuthError("Provided Credential must be " - "impersonated_credentials") + if not isinstance(target_credentials, Credentials): + raise exceptions.GoogleAuthError( + "Provided Credential must be " "impersonated_credentials" + ) self._target_credentials = target_credentials self._target_audience = target_audience self._include_email = include_email - def from_credentials(self, target_credentials, - target_audience=None): + def from_credentials(self, target_credentials, target_audience=None): return self.__class__( - target_credentials=self._target_credentials, - target_audience=target_audience) + target_credentials=self._target_credentials, target_audience=target_audience + ) def with_target_audience(self, target_audience): return self.__class__( - target_credentials=self._target_credentials, - target_audience=target_audience) + target_credentials=self._target_credentials, target_audience=target_audience + ) def with_include_email(self, include_email): return self.__class__( target_credentials=self._target_credentials, target_audience=self._target_audience, - include_email=include_email) + include_email=include_email, + ) @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): - iam_sign_endpoint = _IAM_IDTOKEN_ENDPOINT.format(self. - _target_credentials. - signer_email) + iam_sign_endpoint = _IAM_IDTOKEN_ENDPOINT.format( + self._target_credentials.signer_email + ) body = { "audience": self._target_audience, "delegates": self._target_credentials._delegates, - "includeEmail": self._include_email + "includeEmail": self._include_email, } - headers = { - 'Content-Type': 'application/json', - } + headers = {"Content-Type": "application/json"} - authed_session = AuthorizedSession(self._target_credentials. - _source_credentials) + authed_session = AuthorizedSession(self._target_credentials._source_credentials) response = authed_session.post( url=iam_sign_endpoint, headers=headers, - data=json.dumps(body).encode('utf-8')) + data=json.dumps(body).encode("utf-8"), + ) - id_token = response.json()['token'] + id_token = response.json()["token"] self.token = id_token - self.expiry = datetime.fromtimestamp(jwt.decode(id_token, - verify=False)['exp']) + self.expiry = datetime.fromtimestamp(jwt.decode(id_token, verify=False)["exp"]) diff --git a/google/auth/jwt.py b/google/auth/jwt.py index d63c50bb9..a30c575b2 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -79,36 +79,30 @@ def encode(signer, payload, header=None, key_id=None): if key_id is None: key_id = signer.key_id - header.update({'typ': 'JWT', 'alg': 'RS256'}) + header.update({"typ": "JWT", "alg": "RS256"}) if key_id is not None: - header['kid'] = key_id + header["kid"] = key_id segments = [ - _helpers.unpadded_urlsafe_b64encode( - json.dumps(header).encode('utf-8') - ), - _helpers.unpadded_urlsafe_b64encode( - json.dumps(payload).encode('utf-8') - ), + _helpers.unpadded_urlsafe_b64encode(json.dumps(header).encode("utf-8")), + _helpers.unpadded_urlsafe_b64encode(json.dumps(payload).encode("utf-8")), ] - signing_input = b'.'.join(segments) + signing_input = b".".join(segments) signature = signer.sign(signing_input) - segments.append( - _helpers.unpadded_urlsafe_b64encode(signature) - ) + segments.append(_helpers.unpadded_urlsafe_b64encode(signature)) - return b'.'.join(segments) + return b".".join(segments) def _decode_jwt_segment(encoded_section): """Decodes a single JWT segment.""" section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section) try: - return json.loads(section_bytes.decode('utf-8')) + return json.loads(section_bytes.decode("utf-8")) except ValueError as caught_exc: - new_exc = ValueError('Can\'t parse segment: {0}'.format(section_bytes)) + new_exc = ValueError("Can't parse segment: {0}".format(section_bytes)) six.raise_from(new_exc, caught_exc) @@ -127,12 +121,11 @@ def _unverified_decode(token): """ token = _helpers.to_bytes(token) - if token.count(b'.') != 2: - raise ValueError( - 'Wrong number of segments in token: {0}'.format(token)) + if token.count(b".") != 2: + raise ValueError("Wrong number of segments in token: {0}".format(token)) - encoded_header, encoded_payload, signature = token.split(b'.') - signed_section = encoded_header + b'.' + encoded_payload + encoded_header, encoded_payload, signature = token.split(b".") + signed_section = encoded_header + b"." + encoded_payload signature = _helpers.padded_urlsafe_b64decode(signature) # Parse segments @@ -172,26 +165,25 @@ def _verify_iat_and_exp(payload): now = _helpers.datetime_to_secs(_helpers.utcnow()) # Make sure the iat and exp claims are present. - for key in ('iat', 'exp'): + for key in ("iat", "exp"): if key not in payload: - raise ValueError( - 'Token does not contain required claim {}'.format(key)) + raise ValueError("Token does not contain required claim {}".format(key)) # Make sure the token wasn't issued in the future. - iat = payload['iat'] + iat = payload["iat"] # Err on the side of accepting a token that is slightly early to account # for clock skew. earliest = iat - _helpers.CLOCK_SKEW_SECS if now < earliest: - raise ValueError('Token used too early, {} < {}'.format(now, iat)) + raise ValueError("Token used too early, {} < {}".format(now, iat)) # Make sure the token wasn't issued in the past. - exp = payload['exp'] + exp = payload["exp"] # Err on the side of accepting a token that is slightly out of date # to account for clow skew. latest = exp + _helpers.CLOCK_SKEW_SECS if latest < now: - raise ValueError('Token expired, {} < {}'.format(latest, now)) + raise ValueError("Token expired, {} < {}".format(latest, now)) def decode(token, certs=None, verify=True, audience=None): @@ -224,11 +216,10 @@ def decode(token, certs=None, verify=True, audience=None): # If certs is specified as a dictionary of key IDs to certificates, then # use the certificate identified by the key ID in the token header. if isinstance(certs, collections.Mapping): - key_id = header.get('kid') + key_id = header.get("kid") if key_id: if key_id not in certs: - raise ValueError( - 'Certificate for key id {} not found.'.format(key_id)) + raise ValueError("Certificate for key id {} not found.".format(key_id)) certs_to_check = [certs[key_id]] # If there's no key id in the header, check against all of the certs. else: @@ -238,24 +229,25 @@ def decode(token, certs=None, verify=True, audience=None): # Verify that the signature matches the message. if not crypt.verify_signature(signed_section, signature, certs_to_check): - raise ValueError('Could not verify token signature.') + raise ValueError("Could not verify token signature.") # Verify the issued at and created times in the payload. _verify_iat_and_exp(payload) # Check audience. if audience is not None: - claim_audience = payload.get('aud') + claim_audience = payload.get("aud") if audience != claim_audience: raise ValueError( - 'Token has wrong audience {}, expected {}'.format( - claim_audience, audience)) + "Token has wrong audience {}, expected {}".format( + claim_audience, audience + ) + ) return payload -class Credentials(google.auth.credentials.Signing, - google.auth.credentials.Credentials): +class Credentials(google.auth.credentials.Signing, google.auth.credentials.Credentials): """Credentials that use a JWT as the bearer token. These credentials require an "audience" claim. This claim identifies the @@ -305,9 +297,15 @@ class Credentials(google.auth.credentials.Signing, new_credentials = credentials.with_claims(audience=new_audience) """ - def __init__(self, signer, issuer, subject, audience, - additional_claims=None, - token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS): + def __init__( + self, + signer, + issuer, + subject, + audience, + additional_claims=None, + token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS, + ): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. @@ -348,8 +346,8 @@ def _from_signer_and_info(cls, signer, info, **kwargs): Raises: ValueError: If the info is not in the expected format. """ - kwargs.setdefault('subject', info['client_email']) - kwargs.setdefault('issuer', info['client_email']) + kwargs.setdefault("subject", info["client_email"]) + kwargs.setdefault("issuer", info["client_email"]) return cls(signer, **kwargs) @classmethod @@ -367,8 +365,7 @@ def from_service_account_info(cls, info, **kwargs): Raises: ValueError: If the info is not in the expected format. """ - signer = _service_account_info.from_dict( - info, require=['client_email']) + signer = _service_account_info.from_dict(info, require=["client_email"]) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -384,7 +381,8 @@ def from_service_account_file(cls, filename, **kwargs): google.auth.jwt.Credentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( - filename, require=['client_email']) + filename, require=["client_email"] + ) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -415,15 +413,13 @@ def from_signing_credentials(cls, credentials, audience, **kwargs): Returns: google.auth.jwt.Credentials: A new Credentials instance. """ - kwargs.setdefault('issuer', credentials.signer_email) - kwargs.setdefault('subject', credentials.signer_email) - return cls( - credentials.signer, - audience=audience, - **kwargs) + kwargs.setdefault("issuer", credentials.signer_email) + kwargs.setdefault("subject", credentials.signer_email) + return cls(credentials.signer, audience=audience, **kwargs) - def with_claims(self, issuer=None, subject=None, audience=None, - additional_claims=None): + def with_claims( + self, issuer=None, subject=None, audience=None, additional_claims=None + ): """Returns a copy of these credentials with modified claims. Args: @@ -448,7 +444,8 @@ def with_claims(self, issuer=None, subject=None, audience=None, issuer=issuer if issuer is not None else self._issuer, subject=subject if subject is not None else self._subject, audience=audience if audience is not None else self._audience, - additional_claims=new_additional_claims) + additional_claims=new_additional_claims, + ) def _make_jwt(self): """Make a signed JWT. @@ -461,11 +458,11 @@ def _make_jwt(self): expiry = now + lifetime payload = { - 'iss': self._issuer, - 'sub': self._subject, - 'iat': _helpers.datetime_to_secs(now), - 'exp': _helpers.datetime_to_secs(expiry), - 'aud': self._audience, + "iss": self._issuer, + "sub": self._subject, + "iat": _helpers.datetime_to_secs(now), + "exp": _helpers.datetime_to_secs(expiry), + "aud": self._audience, } payload.update(self._additional_claims) @@ -500,8 +497,8 @@ def signer(self): class OnDemandCredentials( - google.auth.credentials.Signing, - google.auth.credentials.Credentials): + google.auth.credentials.Signing, google.auth.credentials.Credentials +): """On-demand JWT credentials. Like :class:`Credentials`, this class uses a JWT as the bearer token for @@ -519,10 +516,15 @@ class OnDemandCredentials( .. _grpc: http://www.grpc.io/ """ - def __init__(self, signer, issuer, subject, - additional_claims=None, - token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS, - max_cache_size=_DEFAULT_MAX_CACHE_SIZE): + def __init__( + self, + signer, + issuer, + subject, + additional_claims=None, + token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS, + max_cache_size=_DEFAULT_MAX_CACHE_SIZE, + ): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. @@ -563,8 +565,8 @@ def _from_signer_and_info(cls, signer, info, **kwargs): Raises: ValueError: If the info is not in the expected format. """ - kwargs.setdefault('subject', info['client_email']) - kwargs.setdefault('issuer', info['client_email']) + kwargs.setdefault("subject", info["client_email"]) + kwargs.setdefault("issuer", info["client_email"]) return cls(signer, **kwargs) @classmethod @@ -582,8 +584,7 @@ def from_service_account_info(cls, info, **kwargs): Raises: ValueError: If the info is not in the expected format. """ - signer = _service_account_info.from_dict( - info, require=['client_email']) + signer = _service_account_info.from_dict(info, require=["client_email"]) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -599,7 +600,8 @@ def from_service_account_file(cls, filename, **kwargs): google.auth.jwt.OnDemandCredentials: The constructed credentials. """ info, signer = _service_account_info.from_filename( - filename, require=['client_email']) + filename, require=["client_email"] + ) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -626,8 +628,8 @@ def from_signing_credentials(cls, credentials, **kwargs): Returns: google.auth.jwt.Credentials: A new Credentials instance. """ - kwargs.setdefault('issuer', credentials.signer_email) - kwargs.setdefault('subject', credentials.signer_email) + kwargs.setdefault("issuer", credentials.signer_email) + kwargs.setdefault("subject", credentials.signer_email) return cls(credentials.signer, **kwargs) def with_claims(self, issuer=None, subject=None, additional_claims=None): @@ -653,7 +655,8 @@ def with_claims(self, issuer=None, subject=None, additional_claims=None): issuer=issuer if issuer is not None else self._issuer, subject=subject if subject is not None else self._subject, additional_claims=new_additional_claims, - max_cache_size=self._cache.maxsize) + max_cache_size=self._cache.maxsize, + ) @property def valid(self): @@ -678,11 +681,11 @@ def _make_jwt_for_audience(self, audience): expiry = now + lifetime payload = { - 'iss': self._issuer, - 'sub': self._subject, - 'iat': _helpers.datetime_to_secs(now), - 'exp': _helpers.datetime_to_secs(expiry), - 'aud': audience, + "iss": self._issuer, + "sub": self._subject, + "iat": _helpers.datetime_to_secs(now), + "exp": _helpers.datetime_to_secs(expiry), + "aud": audience, } payload.update(self._additional_claims) @@ -725,7 +728,8 @@ def refresh(self, request): # pylint: disable=unused-argument # (pylint doesn't correctly recognize overridden methods.) raise exceptions.RefreshError( - 'OnDemandCredentials can not be directly refreshed.') + "OnDemandCredentials can not be directly refreshed." + ) def before_request(self, request, method, url, headers): """Performs credential-specific before request logic. @@ -743,7 +747,8 @@ def before_request(self, request, method, url, headers): parts = urllib.parse.urlsplit(url) # Strip query string and fragment audience = urllib.parse.urlunsplit( - (parts.scheme, parts.netloc, parts.path, "", "")) + (parts.scheme, parts.netloc, parts.path, "", "") + ) token = self._get_jwt_for_audience(audience) self.apply(headers, token=token) diff --git a/google/auth/transport/__init__.py b/google/auth/transport/__init__.py index d73c63cd7..53aa4ba14 100644 --- a/google/auth/transport/__init__.py +++ b/google/auth/transport/__init__.py @@ -45,17 +45,17 @@ class Response(object): @abc.abstractproperty def status(self): """int: The HTTP status code.""" - raise NotImplementedError('status must be implemented.') + raise NotImplementedError("status must be implemented.") @abc.abstractproperty def headers(self): """Mapping[str, str]: The HTTP response headers.""" - raise NotImplementedError('headers must be implemented.') + raise NotImplementedError("headers must be implemented.") @abc.abstractproperty def data(self): """bytes: The response body.""" - raise NotImplementedError('data must be implemented.') + raise NotImplementedError("data must be implemented.") @six.add_metaclass(abc.ABCMeta) @@ -69,8 +69,9 @@ class Request(object): """ @abc.abstractmethod - def __call__(self, url, method='GET', body=None, headers=None, - timeout=None, **kwargs): + def __call__( + self, url, method="GET", body=None, headers=None, timeout=None, **kwargs + ): """Make an HTTP request. Args: @@ -93,4 +94,4 @@ def __call__(self, url, method='GET', body=None, headers=None, """ # pylint: disable=redundant-returns-doc, missing-raises-doc # (pylint doesn't play well with abstract docstrings.) - raise NotImplementedError('__call__ must be implemented.') + raise NotImplementedError("__call__ must be implemented.") diff --git a/google/auth/transport/_http_client.py b/google/auth/transport/_http_client.py index 08b1ab6c7..b52ca7b50 100644 --- a/google/auth/transport/_http_client.py +++ b/google/auth/transport/_http_client.py @@ -33,10 +33,10 @@ class Response(transport.Response): Args: response (http.client.HTTPResponse): The raw http client response. """ + def __init__(self, response): self._status = response.status - self._headers = { - key.lower(): value for key, value in response.getheaders()} + self._headers = {key.lower(): value for key, value in response.getheaders()} self._data = response.read() @property @@ -55,8 +55,9 @@ def data(self): class Request(transport.Request): """http.client transport request adapter.""" - def __call__(self, url, method='GET', body=None, headers=None, - timeout=None, **kwargs): + def __call__( + self, url, method="GET", body=None, headers=None, timeout=None, **kwargs + ): """Make an HTTP request using http.client. Args: @@ -88,20 +89,21 @@ def __call__(self, url, method='GET', body=None, headers=None, # http.client needs the host and path parts specified separately. parts = urllib.parse.urlsplit(url) path = urllib.parse.urlunsplit( - ('', '', parts.path, parts.query, parts.fragment)) + ("", "", parts.path, parts.query, parts.fragment) + ) - if parts.scheme != 'http': + if parts.scheme != "http": raise exceptions.TransportError( - 'http.client transport only supports the http scheme, {}' - 'was specified'.format(parts.scheme)) + "http.client transport only supports the http scheme, {}" + "was specified".format(parts.scheme) + ) connection = http_client.HTTPConnection(parts.netloc, timeout=timeout) try: - _LOGGER.debug('Making request: %s %s', method, url) + _LOGGER.debug("Making request: %s %s", method, url) - connection.request( - method, path, body=body, headers=headers, **kwargs) + connection.request(method, path, body=body, headers=headers, **kwargs) response = connection.getresponse() return Response(response) diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py index 0d44f6458..9a1bc6d18 100644 --- a/google/auth/transport/grpc.py +++ b/google/auth/transport/grpc.py @@ -17,13 +17,14 @@ from __future__ import absolute_import import six + try: import grpc except ImportError as caught_exc: # pragma: NO COVER six.raise_from( ImportError( - 'gRPC is not installed, please install the grpcio package ' - 'to use the gRPC transport.' + "gRPC is not installed, please install the grpcio package " + "to use the gRPC transport." ), caught_exc, ) @@ -42,6 +43,7 @@ class AuthMetadataPlugin(grpc.AuthMetadataPlugin): request (google.auth.transport.Request): A HTTP transport request object used to refresh credentials as needed. """ + def __init__(self, credentials, request): # pylint: disable=no-value-for-parameter # pylint doesn't realize that the super method takes no arguments @@ -59,10 +61,8 @@ def _get_authorization_headers(self, context): """ headers = {} self._credentials.before_request( - self._request, - context.method_name, - context.service_url, - headers) + self._request, context.method_name, context.service_url, headers + ) return list(six.iteritems(headers)) @@ -78,7 +78,8 @@ def __call__(self, context, callback): def secure_authorized_channel( - credentials, request, target, ssl_credentials=None, **kwargs): + credentials, request, target, ssl_credentials=None, **kwargs +): """Creates a secure authorized gRPC channel. This creates a channel with SSL and :class:`AuthMetadataPlugin`. This @@ -130,6 +131,7 @@ def secure_authorized_channel( # Combine the ssl credentials and the authorization credentials. composite_credentials = grpc.composite_channel_credentials( - ssl_credentials, google_auth_credentials) + ssl_credentials, google_auth_credentials + ) return grpc.secure_channel(target, composite_credentials, **kwargs) diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py index 8250c74c6..564a0cd04 100644 --- a/google/auth/transport/requests.py +++ b/google/auth/transport/requests.py @@ -23,10 +23,11 @@ import requests except ImportError as caught_exc: # pragma: NO COVER import six + six.raise_from( ImportError( - 'The requests library is not installed, please install the ' - 'requests package to use the requests transport.' + "The requests library is not installed, please install the " + "requests package to use the requests transport." ), caught_exc, ) @@ -46,6 +47,7 @@ class _Response(transport.Response): Args: response (requests.Response): The raw Requests response. """ + def __init__(self, response): self._response = response @@ -85,14 +87,16 @@ class Request(transport.Request): .. automethod:: __call__ """ + def __init__(self, session=None): if not session: session = requests.Session() self.session = session - def __call__(self, url, method='GET', body=None, headers=None, - timeout=None, **kwargs): + def __call__( + self, url, method="GET", body=None, headers=None, timeout=None, **kwargs + ): """Make an HTTP request using requests. Args: @@ -114,10 +118,10 @@ def __call__(self, url, method='GET', body=None, headers=None, google.auth.exceptions.TransportError: If any exception occurred. """ try: - _LOGGER.debug('Making request: %s %s', method, url) + _LOGGER.debug("Making request: %s %s", method, url) response = self.session.request( - method, url, data=body, headers=headers, timeout=timeout, - **kwargs) + method, url, data=body, headers=headers, timeout=timeout, **kwargs + ) return _Response(response) except requests.exceptions.RequestException as caught_exc: new_exc = exceptions.TransportError(caught_exc) @@ -157,11 +161,15 @@ class AuthorizedSession(requests.Session): an instance of :class:`~google.auth.transport.requests.Request` is created. """ - def __init__(self, credentials, - refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, - max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS, - refresh_timeout=None, - auth_request=None): + + def __init__( + self, + credentials, + refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, + max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS, + refresh_timeout=None, + auth_request=None, + ): super(AuthorizedSession, self).__init__() self.credentials = credentials self._refresh_status_codes = refresh_status_codes @@ -194,40 +202,50 @@ def request(self, method, url, data=None, headers=None, **kwargs): # Use a kwarg for this instead of an attribute to maintain # thread-safety. - _credential_refresh_attempt = kwargs.pop( - '_credential_refresh_attempt', 0) + _credential_refresh_attempt = kwargs.pop("_credential_refresh_attempt", 0) # Make a copy of the headers. They will be modified by the credentials # and we want to pass the original headers if we recurse. request_headers = headers.copy() if headers is not None else {} self.credentials.before_request( - self._auth_request, method, url, request_headers) + self._auth_request, method, url, request_headers + ) response = super(AuthorizedSession, self).request( - method, url, data=data, headers=request_headers, **kwargs) + method, url, data=data, headers=request_headers, **kwargs + ) # If the response indicated that the credentials needed to be # refreshed, then refresh the credentials and re-attempt the # request. # A stored token may expire between the time it is retrieved and # the time the request is made, so we may need to try twice. - if (response.status_code in self._refresh_status_codes - and _credential_refresh_attempt < self._max_refresh_attempts): + if ( + response.status_code in self._refresh_status_codes + and _credential_refresh_attempt < self._max_refresh_attempts + ): _LOGGER.info( - 'Refreshing credentials due to a %s response. Attempt %s/%s.', - response.status_code, _credential_refresh_attempt + 1, - self._max_refresh_attempts) + "Refreshing credentials due to a %s response. Attempt %s/%s.", + response.status_code, + _credential_refresh_attempt + 1, + self._max_refresh_attempts, + ) auth_request_with_timeout = functools.partial( - self._auth_request, timeout=self._refresh_timeout) + self._auth_request, timeout=self._refresh_timeout + ) self.credentials.refresh(auth_request_with_timeout) # Recurse. Pass in the original headers, not our modified set. return self.request( - method, url, data=data, headers=headers, + method, + url, + data=data, + headers=headers, _credential_refresh_attempt=_credential_refresh_attempt + 1, - **kwargs) + **kwargs + ) return response diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py index 37eb31757..dbb186bb1 100644 --- a/google/auth/transport/urllib3.py +++ b/google/auth/transport/urllib3.py @@ -34,10 +34,11 @@ import urllib3 except ImportError as caught_exc: # pragma: NO COVER import six + six.raise_from( ImportError( - 'The urllib3 library is not installed, please install the ' - 'urllib3 package to use the urllib3 transport.' + "The urllib3 library is not installed, please install the " + "urllib3 package to use the urllib3 transport." ), caught_exc, ) @@ -56,6 +57,7 @@ class _Response(transport.Response): Args: response (urllib3.response.HTTPResponse): The raw urllib3 response. """ + def __init__(self, response): self._response = response @@ -97,11 +99,13 @@ class that implements :class:`~urllib3.request.RequestMethods`, .. automethod:: __call__ """ + def __init__(self, http): self.http = http - def __call__(self, url, method='GET', body=None, headers=None, - timeout=None, **kwargs): + def __call__( + self, url, method="GET", body=None, headers=None, timeout=None, **kwargs + ): """Make an HTTP request using urllib3. Args: @@ -125,12 +129,13 @@ def __call__(self, url, method='GET', body=None, headers=None, # urllib3 uses a sentinel default value for timeout, so only set it if # specified. if timeout is not None: - kwargs['timeout'] = timeout + kwargs["timeout"] = timeout try: - _LOGGER.debug('Making request: %s %s', method, url) + _LOGGER.debug("Making request: %s %s", method, url) response = self.http.request( - method, url, body=body, headers=headers, **kwargs) + method, url, body=body, headers=headers, **kwargs + ) return _Response(response) except urllib3.exceptions.HTTPError as caught_exc: new_exc = exceptions.TransportError(caught_exc) @@ -139,9 +144,7 @@ def __call__(self, url, method='GET', body=None, headers=None, def _make_default_http(): if certifi is not None: - return urllib3.PoolManager( - cert_reqs='CERT_REQUIRED', - ca_certs=certifi.where()) + return urllib3.PoolManager(cert_reqs="CERT_REQUIRED", ca_certs=certifi.where()) else: return urllib3.PoolManager() @@ -178,9 +181,14 @@ class AuthorizedHttp(urllib3.request.RequestMethods): max_refresh_attempts (int): The maximum number of times to attempt to refresh the credentials and retry the request. """ - def __init__(self, credentials, http=None, - refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, - max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS): + + def __init__( + self, + credentials, + http=None, + refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES, + max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS, + ): if http is None: http = _make_default_http() @@ -204,8 +212,7 @@ def urlopen(self, method, url, body=None, headers=None, **kwargs): # Use a kwarg for this instead of an attribute to maintain # thread-safety. - _credential_refresh_attempt = kwargs.pop( - '_credential_refresh_attempt', 0) + _credential_refresh_attempt = kwargs.pop("_credential_refresh_attempt", 0) if headers is None: headers = self.headers @@ -214,11 +221,11 @@ def urlopen(self, method, url, body=None, headers=None, **kwargs): # and we want to pass the original headers if we recurse. request_headers = headers.copy() - self.credentials.before_request( - self._request, method, url, request_headers) + self.credentials.before_request(self._request, method, url, request_headers) response = self.http.urlopen( - method, url, body=body, headers=request_headers, **kwargs) + method, url, body=body, headers=request_headers, **kwargs + ) # If the response indicated that the credentials needed to be # refreshed, then refresh the credentials and re-attempt the @@ -227,21 +234,29 @@ def urlopen(self, method, url, body=None, headers=None, **kwargs): # the time the request is made, so we may need to try twice. # The reason urllib3's retries aren't used is because they # don't allow you to modify the request headers. :/ - if (response.status in self._refresh_status_codes - and _credential_refresh_attempt < self._max_refresh_attempts): + if ( + response.status in self._refresh_status_codes + and _credential_refresh_attempt < self._max_refresh_attempts + ): _LOGGER.info( - 'Refreshing credentials due to a %s response. Attempt %s/%s.', - response.status, _credential_refresh_attempt + 1, - self._max_refresh_attempts) + "Refreshing credentials due to a %s response. Attempt %s/%s.", + response.status, + _credential_refresh_attempt + 1, + self._max_refresh_attempts, + ) self.credentials.refresh(self._request) # Recurse. Pass in the original headers, not our modified set. return self.urlopen( - method, url, body=body, headers=headers, + method, + url, + body=body, + headers=headers, _credential_refresh_attempt=_credential_refresh_attempt + 1, - **kwargs) + **kwargs + ) return response diff --git a/google/oauth2/_client.py b/google/oauth2/_client.py index eac01b7ec..996f9b7e2 100644 --- a/google/oauth2/_client.py +++ b/google/oauth2/_client.py @@ -34,9 +34,9 @@ from google.auth import exceptions from google.auth import jwt -_URLENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded' -_JWT_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer' -_REFRESH_GRANT_TYPE = 'refresh_token' +_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded" +_JWT_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer" +_REFRESH_GRANT_TYPE = "refresh_token" def _handle_error_response(response_body): @@ -50,15 +50,14 @@ def _handle_error_response(response_body): """ try: error_data = json.loads(response_body) - error_details = '{}: {}'.format( - error_data['error'], - error_data.get('error_description')) + error_details = "{}: {}".format( + error_data["error"], error_data.get("error_description") + ) # If no details could be extracted, use the response data. except (KeyError, ValueError): error_details = response_body - raise exceptions.RefreshError( - error_details, response_body) + raise exceptions.RefreshError(error_details, response_body) def _parse_expiry(response_data): @@ -71,11 +70,10 @@ def _parse_expiry(response_data): Optional[datetime]: The expiration or ``None`` if no expiration was specified. """ - expires_in = response_data.get('expires_in', None) + expires_in = response_data.get("expires_in", None) if expires_in is not None: - return _helpers.utcnow() + datetime.timedelta( - seconds=expires_in) + return _helpers.utcnow() + datetime.timedelta(seconds=expires_in) else: return None @@ -98,24 +96,20 @@ def _token_endpoint_request(request, token_uri, body): an error. """ body = urllib.parse.urlencode(body) - headers = { - 'content-type': _URLENCODED_CONTENT_TYPE, - } + headers = {"content-type": _URLENCODED_CONTENT_TYPE} retry = 0 # retry to fetch token for maximum of two times if any internal failure # occurs. while True: - response = request( - method='POST', url=token_uri, headers=headers, body=body) - response_body = response.data.decode('utf-8') + response = request(method="POST", url=token_uri, headers=headers, body=body) + response_body = response.data.decode("utf-8") if response.status == http_client.OK: break else: - error_desc = json.loads( - response_body).get('error_description') or '' - if error_desc == 'internal_failure' and retry < 1: + error_desc = json.loads(response_body).get("error_description") or "" + if error_desc == "internal_failure" and retry < 1: retry += 1 continue _handle_error_response(response_body) @@ -147,18 +141,14 @@ def jwt_grant(request, token_uri, assertion): .. _rfc7523 section 4: https://tools.ietf.org/html/rfc7523#section-4 """ - body = { - 'assertion': assertion, - 'grant_type': _JWT_GRANT_TYPE, - } + body = {"assertion": assertion, "grant_type": _JWT_GRANT_TYPE} response_data = _token_endpoint_request(request, token_uri, body) try: - access_token = response_data['access_token'] + access_token = response_data["access_token"] except KeyError as caught_exc: - new_exc = exceptions.RefreshError( - 'No access token in response.', response_data) + new_exc = exceptions.RefreshError("No access token in response.", response_data) six.raise_from(new_exc, caught_exc) expiry = _parse_expiry(response_data) @@ -191,28 +181,25 @@ def id_token_jwt_grant(request, token_uri, assertion): google.auth.exceptions.RefreshError: If the token endpoint returned an error. """ - body = { - 'assertion': assertion, - 'grant_type': _JWT_GRANT_TYPE, - } + body = {"assertion": assertion, "grant_type": _JWT_GRANT_TYPE} response_data = _token_endpoint_request(request, token_uri, body) try: - id_token = response_data['id_token'] + id_token = response_data["id_token"] except KeyError as caught_exc: - new_exc = exceptions.RefreshError( - 'No ID token in response.', response_data) + new_exc = exceptions.RefreshError("No ID token in response.", response_data) six.raise_from(new_exc, caught_exc) payload = jwt.decode(id_token, verify=False) - expiry = datetime.datetime.utcfromtimestamp(payload['exp']) + expiry = datetime.datetime.utcfromtimestamp(payload["exp"]) return id_token, expiry, response_data -def refresh_grant(request, token_uri, refresh_token, client_id, client_secret, - scopes=None): +def refresh_grant( + request, token_uri, refresh_token, client_id, client_secret, scopes=None +): """Implements the OAuth 2.0 refresh token grant. For more details, see `rfc678 section 6`_. @@ -243,24 +230,23 @@ def refresh_grant(request, token_uri, refresh_token, client_id, client_secret, .. _rfc6748 section 6: https://tools.ietf.org/html/rfc6749#section-6 """ body = { - 'grant_type': _REFRESH_GRANT_TYPE, - 'client_id': client_id, - 'client_secret': client_secret, - 'refresh_token': refresh_token, + "grant_type": _REFRESH_GRANT_TYPE, + "client_id": client_id, + "client_secret": client_secret, + "refresh_token": refresh_token, } if scopes: - body['scope'] = ' '.join(scopes) + body["scope"] = " ".join(scopes) response_data = _token_endpoint_request(request, token_uri, body) try: - access_token = response_data['access_token'] + access_token = response_data["access_token"] except KeyError as caught_exc: - new_exc = exceptions.RefreshError( - 'No access token in response.', response_data) + new_exc = exceptions.RefreshError("No access token in response.", response_data) six.raise_from(new_exc, caught_exc) - refresh_token = response_data.get('refresh_token', refresh_token) + refresh_token = response_data.get("refresh_token", refresh_token) expiry = _parse_expiry(response_data) return access_token, refresh_token, expiry, response_data diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 9e1141646..676a4324e 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -43,15 +43,22 @@ # The Google OAuth 2.0 token endpoint. Used for authorized user credentials. -_GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token' +_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token" class Credentials(credentials.ReadOnlyScoped, credentials.Credentials): """Credentials using OAuth 2.0 access and refresh tokens.""" - def __init__(self, token, refresh_token=None, id_token=None, - token_uri=None, client_id=None, client_secret=None, - scopes=None): + def __init__( + self, + token, + refresh_token=None, + id_token=None, + token_uri=None, + client_id=None, + client_secret=None, + scopes=None, + ): """ Args: token (Optional(str)): The OAuth 2.0 access token. Can be None @@ -124,35 +131,43 @@ def requires_scopes(self): @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): - if (self._refresh_token is None or - self._token_uri is None or - self._client_id is None or - self._client_secret is None): + if ( + self._refresh_token is None + or self._token_uri is None + or self._client_id is None + or self._client_secret is None + ): raise exceptions.RefreshError( - 'The credentials do not contain the necessary fields need to ' - 'refresh the access token. You must specify refresh_token, ' - 'token_uri, client_id, and client_secret.') - - access_token, refresh_token, expiry, grant_response = ( - _client.refresh_grant( - request, self._token_uri, self._refresh_token, self._client_id, - self._client_secret, self._scopes)) + "The credentials do not contain the necessary fields need to " + "refresh the access token. You must specify refresh_token, " + "token_uri, client_id, and client_secret." + ) + + access_token, refresh_token, expiry, grant_response = _client.refresh_grant( + request, + self._token_uri, + self._refresh_token, + self._client_id, + self._client_secret, + self._scopes, + ) self.token = access_token self.expiry = expiry self._refresh_token = refresh_token - self._id_token = grant_response.get('id_token') + self._id_token = grant_response.get("id_token") - if self._scopes and 'scopes' in grant_response: + if self._scopes and "scopes" in grant_response: requested_scopes = frozenset(self._scopes) - granted_scopes = frozenset(grant_response['scopes'].split()) - scopes_requested_but_not_granted = ( - requested_scopes - granted_scopes) + granted_scopes = frozenset(grant_response["scopes"].split()) + scopes_requested_but_not_granted = requested_scopes - granted_scopes if scopes_requested_but_not_granted: raise exceptions.RefreshError( - 'Not all requested scopes were granted by the ' - 'authorization server, missing scopes {}.'.format( - ', '.join(scopes_requested_but_not_granted))) + "Not all requested scopes were granted by the " + "authorization server, missing scopes {}.".format( + ", ".join(scopes_requested_but_not_granted) + ) + ) @classmethod def from_authorized_user_info(cls, info, scopes=None): @@ -171,21 +186,23 @@ def from_authorized_user_info(cls, info, scopes=None): Raises: ValueError: If the info is not in the expected format. """ - keys_needed = set(('refresh_token', 'client_id', 'client_secret')) + keys_needed = set(("refresh_token", "client_id", "client_secret")) missing = keys_needed.difference(six.iterkeys(info)) if missing: raise ValueError( - 'Authorized user info was not in the expected format, missing ' - 'fields {}.'.format(', '.join(missing))) + "Authorized user info was not in the expected format, missing " + "fields {}.".format(", ".join(missing)) + ) return cls( None, # No access token, must be refreshed. - refresh_token=info['refresh_token'], + refresh_token=info["refresh_token"], token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, scopes=scopes, - client_id=info['client_id'], - client_secret=info['client_secret']) + client_id=info["client_id"], + client_secret=info["client_secret"], + ) @classmethod def from_authorized_user_file(cls, filename, scopes=None): @@ -203,6 +220,6 @@ def from_authorized_user_file(cls, filename, scopes=None): Raises: ValueError: If the file is not in the expected format. """ - with io.open(filename, 'r', encoding='utf-8') as json_file: + with io.open(filename, "r", encoding="utf-8") as json_file: data = json.load(json_file) return cls.from_authorized_user_info(data, scopes) diff --git a/google/oauth2/id_token.py b/google/oauth2/id_token.py index 208ab6224..bc4844513 100644 --- a/google/oauth2/id_token.py +++ b/google/oauth2/id_token.py @@ -67,13 +67,14 @@ # The URL that provides public certificates for verifying ID tokens issued # by Google's OAuth 2.0 authorization server. -_GOOGLE_OAUTH2_CERTS_URL = 'https://www.googleapis.com/oauth2/v1/certs' +_GOOGLE_OAUTH2_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs" # The URL that provides public certificates for verifying ID tokens issued # by Firebase and the Google APIs infrastructure _GOOGLE_APIS_CERTS_URL = ( - 'https://www.googleapis.com/robot/v1/metadata/x509' - '/securetoken@system.gserviceaccount.com') + "https://www.googleapis.com/robot/v1/metadata/x509" + "/securetoken@system.gserviceaccount.com" +) def _fetch_certs(request, certs_url): @@ -91,17 +92,17 @@ def _fetch_certs(request, certs_url): Mapping[str, str]: A mapping of public key ID to x.509 certificate data. """ - response = request(certs_url, method='GET') + response = request(certs_url, method="GET") if response.status != http_client.OK: raise exceptions.TransportError( - 'Could not fetch certificates at {}'.format(certs_url)) + "Could not fetch certificates at {}".format(certs_url) + ) - return json.loads(response.data.decode('utf-8')) + return json.loads(response.data.decode("utf-8")) -def verify_token(id_token, request, audience=None, - certs_url=_GOOGLE_OAUTH2_CERTS_URL): +def verify_token(id_token, request, audience=None, certs_url=_GOOGLE_OAUTH2_CERTS_URL): """Verifies an ID token and returns the decoded token. Args: @@ -137,8 +138,8 @@ def verify_oauth2_token(id_token, request, audience=None): Mapping[str, Any]: The decoded token. """ return verify_token( - id_token, request, audience=audience, - certs_url=_GOOGLE_OAUTH2_CERTS_URL) + id_token, request, audience=audience, certs_url=_GOOGLE_OAUTH2_CERTS_URL + ) def verify_firebase_token(id_token, request, audience=None): @@ -156,4 +157,5 @@ def verify_firebase_token(id_token, request, audience=None): Mapping[str, Any]: The decoded token. """ return verify_token( - id_token, request, audience=audience, certs_url=_GOOGLE_APIS_CERTS_URL) + id_token, request, audience=audience, certs_url=_GOOGLE_APIS_CERTS_URL + ) diff --git a/google/oauth2/service_account.py b/google/oauth2/service_account.py index c60c56546..17fdd516d 100644 --- a/google/oauth2/service_account.py +++ b/google/oauth2/service_account.py @@ -82,9 +82,7 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds -class Credentials(credentials.Signing, - credentials.Scoped, - credentials.Credentials): +class Credentials(credentials.Signing, credentials.Scoped, credentials.Credentials): """Service account credentials Usually, you'll create these credentials with one of the helper @@ -116,8 +114,16 @@ class Credentials(credentials.Signing, delegated_credentials = credentials.with_subject(subject) """ - def __init__(self, signer, service_account_email, token_uri, scopes=None, - subject=None, project_id=None, additional_claims=None): + def __init__( + self, + signer, + service_account_email, + token_uri, + scopes=None, + subject=None, + project_id=None, + additional_claims=None, + ): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. @@ -169,9 +175,11 @@ def _from_signer_and_info(cls, signer, info, **kwargs): """ return cls( signer, - service_account_email=info['client_email'], - token_uri=info['token_uri'], - project_id=info.get('project_id'), **kwargs) + service_account_email=info["client_email"], + token_uri=info["token_uri"], + project_id=info.get("project_id"), + **kwargs + ) @classmethod def from_service_account_info(cls, info, **kwargs): @@ -190,7 +198,8 @@ def from_service_account_info(cls, info, **kwargs): ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( - info, require=['client_email', 'token_uri']) + info, require=["client_email", "token_uri"] + ) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -206,7 +215,8 @@ def from_service_account_file(cls, filename, **kwargs): credentials. """ info, signer = _service_account_info.from_filename( - filename, require=['client_email', 'token_uri']) + filename, require=["client_email", "token_uri"] + ) return cls._from_signer_and_info(signer, info, **kwargs) @property @@ -237,7 +247,8 @@ def with_scopes(self, scopes): token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, - additional_claims=self._additional_claims.copy()) + additional_claims=self._additional_claims.copy(), + ) def with_subject(self, subject): """Create a copy of these credentials with the specified subject. @@ -256,7 +267,8 @@ def with_subject(self, subject): token_uri=self._token_uri, subject=subject, project_id=self._project_id, - additional_claims=self._additional_claims.copy()) + additional_claims=self._additional_claims.copy(), + ) def with_claims(self, additional_claims): """Returns a copy of these credentials with modified claims. @@ -280,7 +292,8 @@ def with_claims(self, additional_claims): token_uri=self._token_uri, subject=self._subject, project_id=self._project_id, - additional_claims=new_additional_claims) + additional_claims=new_additional_claims, + ) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. @@ -296,20 +309,20 @@ def _make_authorization_grant_assertion(self): expiry = now + lifetime payload = { - 'iat': _helpers.datetime_to_secs(now), - 'exp': _helpers.datetime_to_secs(expiry), + "iat": _helpers.datetime_to_secs(now), + "exp": _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. - 'iss': self._service_account_email, + "iss": self._service_account_email, # The audience must be the auth token endpoint's URI - 'aud': self._token_uri, - 'scope': _helpers.scopes_to_string(self._scopes or ()) + "aud": self._token_uri, + "scope": _helpers.scopes_to_string(self._scopes or ()), } payload.update(self._additional_claims) # The subject can be a user email for domain-wide delegation. if self._subject: - payload.setdefault('sub', self._subject) + payload.setdefault("sub", self._subject) token = jwt.encode(self._signer, payload) @@ -318,8 +331,7 @@ def _make_authorization_grant_assertion(self): @_helpers.copy_docstring(credentials.Credentials) def refresh(self, request): assertion = self._make_authorization_grant_assertion() - access_token, expiry, _ = _client.jwt_grant( - request, self._token_uri, assertion) + access_token, expiry, _ = _client.jwt_grant(request, self._token_uri, assertion) self.token = access_token self.expiry = expiry @@ -379,8 +391,15 @@ class IDTokenCredentials(credentials.Signing, credentials.Credentials): delegated_credentials = credentials.with_subject(subject) """ - def __init__(self, signer, service_account_email, token_uri, - target_audience, additional_claims=None): + + def __init__( + self, + signer, + service_account_email, + token_uri, + target_audience, + additional_claims=None, + ): """ Args: signer (google.auth.crypt.Signer): The signer used to sign JWTs. @@ -424,8 +443,8 @@ def _from_signer_and_info(cls, signer, info, **kwargs): Raises: ValueError: If the info is not in the expected format. """ - kwargs.setdefault('service_account_email', info['client_email']) - kwargs.setdefault('token_uri', info['token_uri']) + kwargs.setdefault("service_account_email", info["client_email"]) + kwargs.setdefault("token_uri", info["token_uri"]) return cls(signer, **kwargs) @classmethod @@ -445,7 +464,8 @@ def from_service_account_info(cls, info, **kwargs): ValueError: If the info is not in the expected format. """ signer = _service_account_info.from_dict( - info, require=['client_email', 'token_uri']) + info, require=["client_email", "token_uri"] + ) return cls._from_signer_and_info(signer, info, **kwargs) @classmethod @@ -461,7 +481,8 @@ def from_service_account_file(cls, filename, **kwargs): credentials. """ info, signer = _service_account_info.from_filename( - filename, require=['client_email', 'token_uri']) + filename, require=["client_email", "token_uri"] + ) return cls._from_signer_and_info(signer, info, **kwargs) def with_target_audience(self, target_audience): @@ -481,7 +502,8 @@ def with_target_audience(self, target_audience): service_account_email=self._service_account_email, token_uri=self._token_uri, target_audience=target_audience, - additional_claims=self._additional_claims.copy()) + additional_claims=self._additional_claims.copy(), + ) def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. @@ -497,15 +519,15 @@ def _make_authorization_grant_assertion(self): expiry = now + lifetime payload = { - 'iat': _helpers.datetime_to_secs(now), - 'exp': _helpers.datetime_to_secs(expiry), + "iat": _helpers.datetime_to_secs(now), + "exp": _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. - 'iss': self.service_account_email, + "iss": self.service_account_email, # The audience must be the auth token endpoint's URI - 'aud': self._token_uri, + "aud": self._token_uri, # The target audience specifies which service the ID token is # intended for. - 'target_audience': self._target_audience + "target_audience": self._target_audience, } payload.update(self._additional_claims) @@ -518,7 +540,8 @@ def _make_authorization_grant_assertion(self): def refresh(self, request): assertion = self._make_authorization_grant_assertion() access_token, expiry, _ = _client.id_token_jwt_grant( - request, self._token_uri, assertion) + request, self._token_uri, assertion + ) self.token = access_token self.expiry = expiry diff --git a/setup.py b/setup.py index 60c7df43b..d303cb306 100644 --- a/setup.py +++ b/setup.py @@ -19,46 +19,46 @@ DEPENDENCIES = ( - 'cachetools>=2.0.0,<3.2', - 'pyasn1-modules>=0.2.1', - 'rsa>=3.1.4,<4.1', - 'setuptools>=40.3.0', - 'six>=1.9.0', + "cachetools>=2.0.0,<3.2", + "pyasn1-modules>=0.2.1", + "rsa>=3.1.4,<4.1", + "setuptools>=40.3.0", + "six>=1.9.0", ) -with io.open('README.rst', 'r') as fh: +with io.open("README.rst", "r") as fh: long_description = fh.read() setup( - name='google-auth', - version='1.6.3', - author='Google Cloud Platform', - author_email='jonwayne+google-auth@google.com', - description='Google Authentication Library', + name="google-auth", + version="1.6.3", + author="Google Cloud Platform", + author_email="jonwayne+google-auth@google.com", + description="Google Authentication Library", long_description=long_description, - url='https://github.com/GoogleCloudPlatform/google-auth-library-python', - packages=find_packages(exclude=('tests*', 'system_tests*')), - namespace_packages=('google',), + url="https://github.com/GoogleCloudPlatform/google-auth-library-python", + packages=find_packages(exclude=("tests*", "system_tests*")), + namespace_packages=("google",), install_requires=DEPENDENCIES, - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', - license='Apache 2.0', - keywords='google auth oauth client', + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + license="Apache 2.0", + keywords="google auth oauth client", classifiers=[ - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: OS Independent', - 'Topic :: Internet :: WWW/HTTP', + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", + "Operating System :: OS Independent", + "Topic :: Internet :: WWW/HTTP", ], ) diff --git a/system_tests/app_engine_test_app/appengine_config.py b/system_tests/app_engine_test_app/appengine_config.py index da02e10d0..6339909e5 100644 --- a/system_tests/app_engine_test_app/appengine_config.py +++ b/system_tests/app_engine_test_app/appengine_config.py @@ -15,7 +15,7 @@ from google.appengine.ext import vendor # Add any libraries installed in the "lib" folder. -vendor.add('lib') +vendor.add("lib") # Patch os.path.expanduser. This should be fixed in GAE @@ -26,4 +26,5 @@ def patched_expanduser(path): return path + os.path.expanduser = patched_expanduser diff --git a/system_tests/app_engine_test_app/main.py b/system_tests/app_engine_test_app/main.py index 122b505fb..a3354acde 100644 --- a/system_tests/app_engine_test_app/main.py +++ b/system_tests/app_engine_test_app/main.py @@ -42,8 +42,8 @@ Captured output: {} """ -TOKEN_INFO_URL = 'https://www.googleapis.com/oauth2/v3/tokeninfo' -EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email' +TOKEN_INFO_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo" +EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email" HTTP = urllib3.contrib.appengine.AppEngineManager() HTTP_REQUEST = google.auth.transport.urllib3.Request(HTTP) @@ -58,13 +58,13 @@ def test_credentials(): assert scoped_credentials.token is not None # Get token info and verify scope - url = _helpers.update_query(TOKEN_INFO_URL, { - 'access_token': scoped_credentials.token, - }) - response = HTTP_REQUEST(url=url, method='GET') - token_info = json.loads(response.data.decode('utf-8')) + url = _helpers.update_query( + TOKEN_INFO_URL, {"access_token": scoped_credentials.token} + ) + response = HTTP_REQUEST(url=url, method="GET") + token_info = json.loads(response.data.decode("utf-8")) - assert token_info['scope'] == EMAIL_SCOPE + assert token_info["scope"] == EMAIL_SCOPE def test_default(): @@ -90,11 +90,11 @@ def run_test_func(func): with capture() as capsys: try: func() - return True, '' + return True, "" except Exception as exc: output = FAILED_TEST_TMPL.format( - func.func_name, exc, traceback.format_exc(), - capsys.getvalue()) + func.func_name, exc, traceback.format_exc(), capsys.getvalue() + ) return False, output @@ -106,7 +106,7 @@ def run_tests(): otherwise, and any captured output from the tests. """ status = True - output = '' + output = "" tests = (test_credentials, test_default) @@ -120,7 +120,7 @@ def run_tests(): class MainHandler(webapp2.RequestHandler): def get(self): - self.response.headers['content-type'] = 'text/plain' + self.response.headers["content-type"] = "text/plain" status, output = run_tests() @@ -130,6 +130,4 @@ def get(self): self.response.write(output) -app = webapp2.WSGIApplication([ - ('/', MainHandler), -], debug=True) +app = webapp2.WSGIApplication([("/", MainHandler)], debug=True) diff --git a/system_tests/conftest.py b/system_tests/conftest.py index d0f7cc023..3f089c4b4 100644 --- a/system_tests/conftest.py +++ b/system_tests/conftest.py @@ -24,13 +24,13 @@ HERE = os.path.dirname(__file__) -DATA_DIR = os.path.join(HERE, 'data') -SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') -AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') +DATA_DIR = os.path.join(HERE, "data") +SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json") +AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json") URLLIB3_HTTP = urllib3.PoolManager(retries=False) REQUESTS_SESSION = requests.Session() REQUESTS_SESSION.verify = False -TOKEN_INFO_URL = 'https://www.googleapis.com/oauth2/v3/tokeninfo' +TOKEN_INFO_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo" @pytest.fixture @@ -45,33 +45,34 @@ def authorized_user_file(): yield AUTHORIZED_USER_FILE -@pytest.fixture(params=['urllib3', 'requests']) +@pytest.fixture(params=["urllib3", "requests"]) def http_request(request): """A transport.request object.""" - if request.param == 'urllib3': + if request.param == "urllib3": yield google.auth.transport.urllib3.Request(URLLIB3_HTTP) - elif request.param == 'requests': + elif request.param == "requests": yield google.auth.transport.requests.Request(REQUESTS_SESSION) @pytest.fixture def token_info(http_request): """Returns a function that obtains OAuth2 token info.""" + def _token_info(access_token=None, id_token=None): query_params = {} if access_token is not None: - query_params['access_token'] = access_token + query_params["access_token"] = access_token elif id_token is not None: - query_params['id_token'] = id_token + query_params["id_token"] = id_token else: - raise ValueError('No token specified.') + raise ValueError("No token specified.") url = _helpers.update_query(TOKEN_INFO_URL, query_params) - response = http_request(url=url, method='GET') + response = http_request(url=url, method="GET") - return json.loads(response.data.decode('utf-8')) + return json.loads(response.data.decode("utf-8")) yield _token_info @@ -79,9 +80,10 @@ def _token_info(access_token=None, id_token=None): @pytest.fixture def verify_refresh(http_request): """Returns a function that verifies that credentials can be refreshed.""" + def _verify_refresh(credentials): if credentials.requires_scopes: - credentials = credentials.with_scopes(['email', 'profile']) + credentials = credentials.with_scopes(["email", "profile"]) credentials.refresh(http_request) @@ -95,8 +97,9 @@ def verify_environment(): """Checks to make sure that requisite data files are available.""" if not os.path.isdir(DATA_DIR): raise EnvironmentError( - 'In order to run system tests, test data must exist in ' - 'system_tests/data. See CONTRIBUTING.rst for details.') + "In order to run system tests, test data must exist in " + "system_tests/data. See CONTRIBUTING.rst for details." + ) def pytest_configure(config): diff --git a/system_tests/noxfile.py b/system_tests/noxfile.py index fa0422ad2..5f9291a2f 100644 --- a/system_tests/noxfile.py +++ b/system_tests/noxfile.py @@ -30,31 +30,31 @@ HERE = os.path.abspath(os.path.dirname(__file__)) -DATA_DIR = os.path.join(HERE, 'data') -SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') -AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') -EXPLICIT_CREDENTIALS_ENV = 'GOOGLE_APPLICATION_CREDENTIALS' -EXPLICIT_PROJECT_ENV = 'GOOGLE_CLOUD_PROJECT' -EXPECT_PROJECT_ENV = 'EXPECT_PROJECT_ID' +DATA_DIR = os.path.join(HERE, "data") +SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json") +AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json") +EXPLICIT_CREDENTIALS_ENV = "GOOGLE_APPLICATION_CREDENTIALS" +EXPLICIT_PROJECT_ENV = "GOOGLE_CLOUD_PROJECT" +EXPECT_PROJECT_ENV = "EXPECT_PROJECT_ID" -SKIP_GAE_TEST_ENV = 'SKIP_APP_ENGINE_SYSTEM_TEST' -GAE_APP_URL_TMPL = 'https://{}-dot-{}.appspot.com' -GAE_TEST_APP_SERVICE = 'google-auth-system-tests' +SKIP_GAE_TEST_ENV = "SKIP_APP_ENGINE_SYSTEM_TEST" +GAE_APP_URL_TMPL = "https://{}-dot-{}.appspot.com" +GAE_TEST_APP_SERVICE = "google-auth-system-tests" # The download location for the Cloud SDK -CLOUD_SDK_DIST_FILENAME = 'google-cloud-sdk.tar.gz' -CLOUD_SDK_DOWNLOAD_URL = ( - 'https://dl.google.com/dl/cloudsdk/release/{}'.format( - CLOUD_SDK_DIST_FILENAME)) +CLOUD_SDK_DIST_FILENAME = "google-cloud-sdk.tar.gz" +CLOUD_SDK_DOWNLOAD_URL = "https://dl.google.com/dl/cloudsdk/release/{}".format( + CLOUD_SDK_DIST_FILENAME +) # This environment variable is recognized by the Cloud SDK and overrides # the location of the SDK's configuration files (which is usually at # ${HOME}/.config). -CLOUD_SDK_CONFIG_ENV = 'CLOUDSDK_CONFIG' +CLOUD_SDK_CONFIG_ENV = "CLOUDSDK_CONFIG" # If set, this is where the environment setup will install the Cloud SDK. # If unset, it will download the SDK to a temporary directory. -CLOUD_SDK_ROOT = os.environ.get('CLOUD_SDK_ROOT') +CLOUD_SDK_ROOT = os.environ.get("CLOUD_SDK_ROOT") if CLOUD_SDK_ROOT is not None: CLOUD_SDK_ROOT = py.path.local(CLOUD_SDK_ROOT) @@ -63,15 +63,15 @@ CLOUD_SDK_ROOT = py.path.local.mkdtemp() # The full path the cloud sdk install directory -CLOUD_SDK_INSTALL_DIR = CLOUD_SDK_ROOT.join('google-cloud-sdk') +CLOUD_SDK_INSTALL_DIR = CLOUD_SDK_ROOT.join("google-cloud-sdk") # The full path to the gcloud cli executable. -GCLOUD = str(CLOUD_SDK_INSTALL_DIR.join('bin', 'gcloud')) +GCLOUD = str(CLOUD_SDK_INSTALL_DIR.join("bin", "gcloud")) # gcloud requires Python 2 and doesn't work on 3, so we need to tell it # where to find 2 when we're running in a 3 environment. -CLOUD_SDK_PYTHON_ENV = 'CLOUDSDK_PYTHON' -CLOUD_SDK_PYTHON = which('python2', None) +CLOUD_SDK_PYTHON_ENV = "CLOUDSDK_PYTHON" +CLOUD_SDK_PYTHON = which("python2", None) # Cloud SDK helpers @@ -87,46 +87,47 @@ def install_cloud_sdk(session): session.env[CLOUD_SDK_PYTHON_ENV] = CLOUD_SDK_PYTHON # This set the $PATH for the subprocesses so they can find the gcloud # executable. - session.env['PATH'] = ( - str(CLOUD_SDK_INSTALL_DIR.join('bin')) + os.pathsep + - os.environ['PATH']) + session.env["PATH"] = ( + str(CLOUD_SDK_INSTALL_DIR.join("bin")) + os.pathsep + os.environ["PATH"] + ) # If gcloud cli executable already exists, just update it. if py.path.local(GCLOUD).exists(): - session.run(GCLOUD, 'components', 'update', '-q') + session.run(GCLOUD, "components", "update", "-q") return tar_path = CLOUD_SDK_ROOT.join(CLOUD_SDK_DIST_FILENAME) # Download the release. - session.run( - 'wget', CLOUD_SDK_DOWNLOAD_URL, '-O', str(tar_path), silent=True) + session.run("wget", CLOUD_SDK_DOWNLOAD_URL, "-O", str(tar_path), silent=True) # Extract the release. - session.run( - 'tar', 'xzf', str(tar_path), '-C', str(CLOUD_SDK_ROOT)) + session.run("tar", "xzf", str(tar_path), "-C", str(CLOUD_SDK_ROOT)) session.run(tar_path.remove) # Run the install script. session.run( - str(CLOUD_SDK_INSTALL_DIR.join('install.sh')), - '--usage-reporting', 'false', - '--path-update', 'false', - '--command-completion', 'false', - silent=True) + str(CLOUD_SDK_INSTALL_DIR.join("install.sh")), + "--usage-reporting", + "false", + "--path-update", + "false", + "--command-completion", + "false", + silent=True, + ) def copy_credentials(credentials_path): """Copies credentials into the SDK root as the application default credentials.""" - dest = CLOUD_SDK_ROOT.join('application_default_credentials.json') + dest = CLOUD_SDK_ROOT.join("application_default_credentials.json") if dest.exists(): dest.remove() py.path.local(credentials_path).copy(dest) -def configure_cloud_sdk( - session, application_default_credentials, project=False): +def configure_cloud_sdk(session, application_default_credentials, project=False): """Installs and configures the Cloud SDK with the given application default credentials. @@ -140,13 +141,13 @@ def configure_cloud_sdk( # change the application default credentials file, which is user # credentials instead of service account credentials sometimes. session.run( - GCLOUD, 'auth', 'activate-service-account', '--key-file', - SERVICE_ACCOUNT_FILE) + GCLOUD, "auth", "activate-service-account", "--key-file", SERVICE_ACCOUNT_FILE + ) if project: - session.run(GCLOUD, 'config', 'set', 'project', 'example-project') + session.run(GCLOUD, "config", "set", "project", "example-project") else: - session.run(GCLOUD, 'config', 'unset', 'project') + session.run(GCLOUD, "config", "unset", "project") # Copy the credentials file to the config root. This is needed because # unfortunately gcloud doesn't provide a clean way to tell it to use @@ -160,8 +161,8 @@ def configure_cloud_sdk( # that our credentials matches the format expected by gcloud. # Silent is set to True to prevent leaking secrets in test logs. session.run( - GCLOUD, 'auth', 'application-default', 'print-access-token', - silent=True) + GCLOUD, "auth", "application-default", "print-access-token", silent=True + ) # Test sesssions @@ -169,99 +170,102 @@ def configure_cloud_sdk( def session_service_account(session): session.virtualenv = False - session.run('pytest', 'test_service_account.py') + session.run("pytest", "test_service_account.py") def session_oauth2_credentials(session): session.virtualenv = False - session.run('pytest', 'test_oauth2_credentials.py') + session.run("pytest", "test_oauth2_credentials.py") def session_default_explicit_service_account(session): session.virtualenv = False session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE - session.env[EXPECT_PROJECT_ENV] = '1' - session.run('pytest', 'test_default.py') + session.env[EXPECT_PROJECT_ENV] = "1" + session.run("pytest", "test_default.py") def session_default_explicit_authorized_user(session): session.virtualenv = False session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE - session.run('pytest', 'test_default.py') + session.run("pytest", "test_default.py") def session_default_explicit_authorized_user_explicit_project(session): session.virtualenv = False session.env[EXPLICIT_CREDENTIALS_ENV] = AUTHORIZED_USER_FILE - session.env[EXPLICIT_PROJECT_ENV] = 'example-project' - session.env[EXPECT_PROJECT_ENV] = '1' - session.run('pytest', 'test_default.py') + session.env[EXPLICIT_PROJECT_ENV] = "example-project" + session.env[EXPECT_PROJECT_ENV] = "1" + session.run("pytest", "test_default.py") def session_default_cloud_sdk_service_account(session): session.virtualenv = False configure_cloud_sdk(session, SERVICE_ACCOUNT_FILE) - session.env[EXPECT_PROJECT_ENV] = '1' - session.run('pytest', 'test_default.py') + session.env[EXPECT_PROJECT_ENV] = "1" + session.run("pytest", "test_default.py") def session_default_cloud_sdk_authorized_user(session): session.virtualenv = False configure_cloud_sdk(session, AUTHORIZED_USER_FILE) - session.run('pytest', 'test_default.py') + session.run("pytest", "test_default.py") def session_default_cloud_sdk_authorized_user_configured_project(session): session.virtualenv = False configure_cloud_sdk(session, AUTHORIZED_USER_FILE, project=True) - session.env[EXPECT_PROJECT_ENV] = '1' - session.run('pytest', 'test_default.py') + session.env[EXPECT_PROJECT_ENV] = "1" + session.run("pytest", "test_default.py") def session_compute_engine(session): session.virtualenv = False - session.run('pytest', 'test_compute_engine.py') + session.run("pytest", "test_compute_engine.py") def session_app_engine(session): session.virtualenv = False if SKIP_GAE_TEST_ENV in os.environ: - session.log('Skipping App Engine tests.') + session.log("Skipping App Engine tests.") return # Unlike the default tests above, the App Engine system test require a # 'real' gcloud sdk installation that is configured to deploy to an # app engine project. # Grab the project ID from the cloud sdk. - project_id = subprocess.check_output([ - 'gcloud', 'config', 'list', 'project', '--format', - 'value(core.project)']).decode('utf-8').strip() + project_id = ( + subprocess.check_output( + ["gcloud", "config", "list", "project", "--format", "value(core.project)"] + ) + .decode("utf-8") + .strip() + ) if not project_id: session.error( - 'The Cloud SDK must be installed and configured to deploy to App ' - 'Engine.') + "The Cloud SDK must be installed and configured to deploy to App " "Engine." + ) - application_url = GAE_APP_URL_TMPL.format( - GAE_TEST_APP_SERVICE, project_id) + application_url = GAE_APP_URL_TMPL.format(GAE_TEST_APP_SERVICE, project_id) # Vendor in the test application's dependencies - session.chdir(os.path.join(HERE, 'app_engine_test_app')) + session.chdir(os.path.join(HERE, "app_engine_test_app")) session.run( - 'pip', 'install', '--target', 'lib', '-r', 'requirements.txt', - silent=True) + "pip", "install", "--target", "lib", "-r", "requirements.txt", silent=True + ) # Deploy the application. - session.run('gcloud', 'app', 'deploy', '-q', 'app.yaml') + session.run("gcloud", "app", "deploy", "-q", "app.yaml") # Run the tests - session.env['TEST_APP_URL'] = application_url + session.env["TEST_APP_URL"] = application_url session.chdir(HERE) - session.run('pytest', 'test_app_engine.py') + session.run("pytest", "test_app_engine.py") def session_grpc(session): session.virtualenv = False session.env[EXPLICIT_CREDENTIALS_ENV] = SERVICE_ACCOUNT_FILE - session.run('pytest', 'test_grpc.py') + session.run("pytest", "test_grpc.py") diff --git a/system_tests/test_app_engine.py b/system_tests/test_app_engine.py index 834f9c870..cdf2be436 100644 --- a/system_tests/test_app_engine.py +++ b/system_tests/test_app_engine.py @@ -14,9 +14,9 @@ import os -TEST_APP_URL = os.environ['TEST_APP_URL'] +TEST_APP_URL = os.environ["TEST_APP_URL"] def test_live_application(http_request): - response = http_request(method='GET', url=TEST_APP_URL) - assert response.status == 200, response.data.decode('utf-8') + response = http_request(method="GET", url=TEST_APP_URL) + assert response.status == 200, response.data.decode("utf-8") diff --git a/system_tests/test_compute_engine.py b/system_tests/test_compute_engine.py index 38733276e..3fd420c1e 100644 --- a/system_tests/test_compute_engine.py +++ b/system_tests/test_compute_engine.py @@ -26,7 +26,7 @@ def check_gce_environment(http_request): try: _metadata.get_service_account_info(http_request) except exceptions.TransportError: - pytest.skip('Compute Engine metadata service is not available.') + pytest.skip("Compute Engine metadata service is not available.") def test_refresh(http_request, token_info): @@ -38,7 +38,7 @@ def test_refresh(http_request, token_info): assert credentials.service_account_email is not None info = token_info(credentials.token) - info_scopes = _helpers.string_to_scopes(info['scope']) + info_scopes = _helpers.string_to_scopes(info["scope"]) assert set(info_scopes) == set(credentials.scopes) diff --git a/system_tests/test_default.py b/system_tests/test_default.py index 23f654351..22213e60f 100644 --- a/system_tests/test_default.py +++ b/system_tests/test_default.py @@ -16,7 +16,7 @@ import google.auth -EXPECT_PROJECT_ID = os.environ.get('EXPECT_PROJECT_ID') +EXPECT_PROJECT_ID = os.environ.get("EXPECT_PROJECT_ID") def test_application_default_credentials(verify_refresh): diff --git a/system_tests/test_grpc.py b/system_tests/test_grpc.py index 365bc91d3..ea528307c 100644 --- a/system_tests/test_grpc.py +++ b/system_tests/test_grpc.py @@ -22,61 +22,58 @@ def test_grpc_request_with_regular_credentials(http_request): credentials, project_id = google.auth.default() credentials = google.auth.credentials.with_scopes_if_required( - credentials, ['https://www.googleapis.com/auth/pubsub']) + credentials, ["https://www.googleapis.com/auth/pubsub"] + ) channel = google.auth.transport.grpc.secure_authorized_channel( - credentials, - http_request, - publisher_client.PublisherClient.SERVICE_ADDRESS) + credentials, http_request, publisher_client.PublisherClient.SERVICE_ADDRESS + ) # Create a pub/sub client. client = publisher_client.PublisherClient(channel=channel) # list the topics and drain the iterator to test that an authorized API # call works. - list_topics_iter = client.list_topics( - project='projects/{}'.format(project_id)) + list_topics_iter = client.list_topics(project="projects/{}".format(project_id)) list(list_topics_iter) def test_grpc_request_with_jwt_credentials(): credentials, project_id = google.auth.default() - audience = 'https://{}/google.pubsub.v1.Publisher'.format( - publisher_client.PublisherClient.SERVICE_ADDRESS) + audience = "https://{}/google.pubsub.v1.Publisher".format( + publisher_client.PublisherClient.SERVICE_ADDRESS + ) credentials = google.auth.jwt.Credentials.from_signing_credentials( - credentials, - audience=audience) + credentials, audience=audience + ) channel = google.auth.transport.grpc.secure_authorized_channel( - credentials, - None, - publisher_client.PublisherClient.SERVICE_ADDRESS) + credentials, None, publisher_client.PublisherClient.SERVICE_ADDRESS + ) # Create a pub/sub client. client = publisher_client.PublisherClient(channel=channel) # list the topics and drain the iterator to test that an authorized API # call works. - list_topics_iter = client.list_topics( - project='projects/{}'.format(project_id)) + list_topics_iter = client.list_topics(project="projects/{}".format(project_id)) list(list_topics_iter) def test_grpc_request_with_on_demand_jwt_credentials(): credentials, project_id = google.auth.default() credentials = google.auth.jwt.OnDemandCredentials.from_signing_credentials( - credentials) + credentials + ) channel = google.auth.transport.grpc.secure_authorized_channel( - credentials, - None, - publisher_client.PublisherClient.SERVICE_ADDRESS) + credentials, None, publisher_client.PublisherClient.SERVICE_ADDRESS + ) # Create a pub/sub client. client = publisher_client.PublisherClient(channel=channel) # list the topics and drain the iterator to test that an authorized API # call works. - list_topics_iter = client.list_topics( - project='projects/{}'.format(project_id)) + list_topics_iter = client.list_topics(project="projects/{}".format(project_id)) list(list_topics_iter) diff --git a/system_tests/test_oauth2_credentials.py b/system_tests/test_oauth2_credentials.py index ded0630d4..a33b89fba 100644 --- a/system_tests/test_oauth2_credentials.py +++ b/system_tests/test_oauth2_credentials.py @@ -17,19 +17,20 @@ from google.auth import _helpers import google.oauth2.credentials -GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token' +GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://accounts.google.com/o/oauth2/token" def test_refresh(authorized_user_file, http_request, token_info): - with open(authorized_user_file, 'r') as fh: + with open(authorized_user_file, "r") as fh: info = json.load(fh) credentials = google.oauth2.credentials.Credentials( None, # No access token, must be refreshed. - refresh_token=info['refresh_token'], + refresh_token=info["refresh_token"], token_uri=GOOGLE_OAUTH2_TOKEN_ENDPOINT, - client_id=info['client_id'], - client_secret=info['client_secret']) + client_id=info["client_id"], + client_secret=info["client_secret"], + ) credentials.refresh(http_request) @@ -37,7 +38,10 @@ def test_refresh(authorized_user_file, http_request, token_info): info = token_info(credentials.token) - info_scopes = _helpers.string_to_scopes(info['scope']) - assert set(info_scopes) == set([ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile']) + info_scopes = _helpers.string_to_scopes(info["scope"]) + assert set(info_scopes) == set( + [ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ] + ) diff --git a/system_tests/test_service_account.py b/system_tests/test_service_account.py index aad1497e9..793760199 100644 --- a/system_tests/test_service_account.py +++ b/system_tests/test_service_account.py @@ -21,8 +21,7 @@ @pytest.fixture def credentials(service_account_file): - yield service_account.Credentials.from_service_account_file( - service_account_file) + yield service_account.Credentials.from_service_account_file(service_account_file) def test_refresh_no_scopes(http_request, credentials): @@ -31,7 +30,7 @@ def test_refresh_no_scopes(http_request, credentials): def test_refresh_success(http_request, credentials, token_info): - credentials = credentials.with_scopes(['email', 'profile']) + credentials = credentials.with_scopes(["email", "profile"]) credentials.refresh(http_request) @@ -39,8 +38,11 @@ def test_refresh_success(http_request, credentials, token_info): info = token_info(credentials.token) - assert info['email'] == credentials.service_account_email - info_scopes = _helpers.string_to_scopes(info['scope']) - assert set(info_scopes) == set([ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile']) + assert info["email"] == credentials.service_account_email + info_scopes = _helpers.string_to_scopes(info["scope"]) + assert set(info_scopes) == set( + [ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ] + ) diff --git a/tests/compute_engine/test__metadata.py b/tests/compute_engine/test__metadata.py index bf48882ca..bd06b7402 100644 --- a/tests/compute_engine/test__metadata.py +++ b/tests/compute_engine/test__metadata.py @@ -27,7 +27,7 @@ from google.auth import transport from google.auth.compute_engine import _metadata -PATH = 'instance/service-accounts/default' +PATH = "instance/service-accounts/default" def make_request(data, status=http_client.OK, headers=None): @@ -43,35 +43,35 @@ def make_request(data, status=http_client.OK, headers=None): def test_ping_success(): - request = make_request('', headers=_metadata._METADATA_HEADERS) + request = make_request("", headers=_metadata._METADATA_HEADERS) assert _metadata.ping(request) request.assert_called_once_with( - method='GET', + method="GET", url=_metadata._METADATA_IP_ROOT, headers=_metadata._METADATA_HEADERS, - timeout=_metadata._METADATA_DEFAULT_TIMEOUT) + timeout=_metadata._METADATA_DEFAULT_TIMEOUT, + ) def test_ping_failure_bad_flavor(): - request = make_request( - '', headers={_metadata._METADATA_FLAVOR_HEADER: 'meep'}) + request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"}) assert not _metadata.ping(request) def test_ping_failure_connection_failed(): - request = make_request('') + request = make_request("") request.side_effect = exceptions.TransportError() assert not _metadata.ping(request) def test_ping_success_custom_root(): - request = make_request('', headers=_metadata._METADATA_HEADERS) + request = make_request("", headers=_metadata._METADATA_HEADERS) - fake_ip = '1.2.3.4' + fake_ip = "1.2.3.4" os.environ[environment_vars.GCE_METADATA_IP] = fake_ip reload_module(_metadata) @@ -82,46 +82,47 @@ def test_ping_success_custom_root(): reload_module(_metadata) request.assert_called_once_with( - method='GET', - url='http://' + fake_ip, + method="GET", + url="http://" + fake_ip, headers=_metadata._METADATA_HEADERS, - timeout=_metadata._METADATA_DEFAULT_TIMEOUT) + timeout=_metadata._METADATA_DEFAULT_TIMEOUT, + ) def test_get_success_json(): - key, value = 'foo', 'bar' + key, value = "foo", "bar" data = json.dumps({key: value}) - request = make_request( - data, headers={'content-type': 'application/json'}) + request = make_request(data, headers={"content-type": "application/json"}) result = _metadata.get(request, PATH) request.assert_called_once_with( - method='GET', + method="GET", url=_metadata._METADATA_ROOT + PATH, - headers=_metadata._METADATA_HEADERS) + headers=_metadata._METADATA_HEADERS, + ) assert result[key] == value def test_get_success_text(): - data = 'foobar' - request = make_request(data, headers={'content-type': 'text/plain'}) + data = "foobar" + request = make_request(data, headers={"content-type": "text/plain"}) result = _metadata.get(request, PATH) request.assert_called_once_with( - method='GET', + method="GET", url=_metadata._METADATA_ROOT + PATH, - headers=_metadata._METADATA_HEADERS) + headers=_metadata._METADATA_HEADERS, + ) assert result == data def test_get_success_custom_root(): - request = make_request( - '{}', headers={'content-type': 'application/json'}) + request = make_request("{}", headers={"content-type": "application/json"}) - fake_root = 'another.metadata.service' + fake_root = "another.metadata.service" os.environ[environment_vars.GCE_METADATA_ROOT] = fake_root reload_module(_metadata) @@ -132,83 +133,87 @@ def test_get_success_custom_root(): reload_module(_metadata) request.assert_called_once_with( - method='GET', - url='http://{}/computeMetadata/v1/{}'.format(fake_root, PATH), - headers=_metadata._METADATA_HEADERS) + method="GET", + url="http://{}/computeMetadata/v1/{}".format(fake_root, PATH), + headers=_metadata._METADATA_HEADERS, + ) def test_get_failure(): - request = make_request( - 'Metadata error', status=http_client.NOT_FOUND) + request = make_request("Metadata error", status=http_client.NOT_FOUND) with pytest.raises(exceptions.TransportError) as excinfo: _metadata.get(request, PATH) - assert excinfo.match(r'Metadata error') + assert excinfo.match(r"Metadata error") request.assert_called_once_with( - method='GET', + method="GET", url=_metadata._METADATA_ROOT + PATH, - headers=_metadata._METADATA_HEADERS) + headers=_metadata._METADATA_HEADERS, + ) def test_get_failure_bad_json(): - request = make_request( - '{', headers={'content-type': 'application/json'}) + request = make_request("{", headers={"content-type": "application/json"}) with pytest.raises(exceptions.TransportError) as excinfo: _metadata.get(request, PATH) - assert excinfo.match(r'invalid JSON') + assert excinfo.match(r"invalid JSON") request.assert_called_once_with( - method='GET', + method="GET", url=_metadata._METADATA_ROOT + PATH, - headers=_metadata._METADATA_HEADERS) + headers=_metadata._METADATA_HEADERS, + ) def test_get_project_id(): - project = 'example-project' - request = make_request( - project, headers={'content-type': 'text/plain'}) + project = "example-project" + request = make_request(project, headers={"content-type": "text/plain"}) project_id = _metadata.get_project_id(request) request.assert_called_once_with( - method='GET', - url=_metadata._METADATA_ROOT + 'project/project-id', - headers=_metadata._METADATA_HEADERS) + method="GET", + url=_metadata._METADATA_ROOT + "project/project-id", + headers=_metadata._METADATA_HEADERS, + ) assert project_id == project -@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) +@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test_get_service_account_token(utcnow): ttl = 500 request = make_request( - json.dumps({'access_token': 'token', 'expires_in': ttl}), - headers={'content-type': 'application/json'}) + json.dumps({"access_token": "token", "expires_in": ttl}), + headers={"content-type": "application/json"}, + ) token, expiry = _metadata.get_service_account_token(request) request.assert_called_once_with( - method='GET', - url=_metadata._METADATA_ROOT + PATH + '/token', - headers=_metadata._METADATA_HEADERS) - assert token == 'token' + method="GET", + url=_metadata._METADATA_ROOT + PATH + "/token", + headers=_metadata._METADATA_HEADERS, + ) + assert token == "token" assert expiry == utcnow() + datetime.timedelta(seconds=ttl) def test_get_service_account_info(): - key, value = 'foo', 'bar' + key, value = "foo", "bar" request = make_request( - json.dumps({key: value}), - headers={'content-type': 'application/json'}) + json.dumps({key: value}), headers={"content-type": "application/json"} + ) info = _metadata.get_service_account_info(request) request.assert_called_once_with( - method='GET', - url=_metadata._METADATA_ROOT + PATH + '/?recursive=true', - headers=_metadata._METADATA_HEADERS) + method="GET", + url=_metadata._METADATA_ROOT + PATH + "/?recursive=true", + headers=_metadata._METADATA_HEADERS, + ) assert info[key] == value diff --git a/tests/compute_engine/test_credentials.py b/tests/compute_engine/test_credentials.py index ee415db98..ec9d13b63 100644 --- a/tests/compute_engine/test_credentials.py +++ b/tests/compute_engine/test_credentials.py @@ -38,68 +38,72 @@ def test_default_state(self): # Scopes aren't needed assert not self.credentials.requires_scopes # Service account email hasn't been populated - assert self.credentials.service_account_email == 'default' + assert self.credentials.service_account_email == "default" @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.min + _helpers.CLOCK_SKEW, + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) def test_refresh_success(self, get, utcnow): - get.side_effect = [{ - # First request is for sevice account info. - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }, { - # Second request is for the token. - 'access_token': 'token', - 'expires_in': 500 - }] + get.side_effect = [ + { + # First request is for sevice account info. + "email": "service-account@example.com", + "scopes": ["one", "two"], + }, + { + # Second request is for the token. + "access_token": "token", + "expires_in": 500, + }, + ] # Refresh credentials self.credentials.refresh(None) # Check that the credentials have the token and proper expiration - assert self.credentials.token == 'token' - assert self.credentials.expiry == ( - utcnow() + datetime.timedelta(seconds=500)) + assert self.credentials.token == "token" + assert self.credentials.expiry == (utcnow() + datetime.timedelta(seconds=500)) # Check the credential info - assert (self.credentials.service_account_email == - 'service-account@example.com') - assert self.credentials._scopes == ['one', 'two'] + assert self.credentials.service_account_email == "service-account@example.com" + assert self.credentials._scopes == ["one", "two"] # Check that the credentials are valid (have a token and are not # expired) assert self.credentials.valid - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) def test_refresh_error(self, get): - get.side_effect = exceptions.TransportError('http error') + get.side_effect = exceptions.TransportError("http error") with pytest.raises(exceptions.RefreshError) as excinfo: self.credentials.refresh(None) - assert excinfo.match(r'http error') + assert excinfo.match(r"http error") - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) def test_before_request_refreshes(self, get): - get.side_effect = [{ - # First request is for sevice account info. - 'email': 'service-account@example.com', - 'scopes': 'one two' - }, { - # Second request is for the token. - 'access_token': 'token', - 'expires_in': 500 - }] + get.side_effect = [ + { + # First request is for sevice account info. + "email": "service-account@example.com", + "scopes": "one two", + }, + { + # Second request is for the token. + "access_token": "token", + "expires_in": 500, + }, + ] # Credentials should start as invalid assert not self.credentials.valid # before_request should cause a refresh request = mock.create_autospec(transport.Request, instance=True) - self.credentials.before_request( - request, 'GET', 'http://example.com?a=1#3', {}) + self.credentials.before_request(request, "GET", "http://example.com?a=1#3", {}) # The refresh endpoint should've been called. assert get.called @@ -111,201 +115,207 @@ def test_before_request_refreshes(self, get): class TestIDTokenCredentials(object): credentials = None - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) def test_default_state(self, get): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scope': ['one', 'two'], - }] + get.side_effect = [ + {"email": "service-account@example.com", "scope": ["one", "two"]} + ] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://example.com") + request=request, target_audience="https://example.com" + ) assert not self.credentials.valid # Expiration hasn't been set yet assert not self.credentials.expired # Service account email hasn't been populated - assert (self.credentials.service_account_email - == 'service-account@example.com') + assert self.credentials.service_account_email == "service-account@example.com" # Signer is initialized assert self.credentials.signer - assert self.credentials.signer_email == 'service-account@example.com' + assert self.credentials.signer_email == "service-account@example.com" @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_make_authorization_grant_assertion(self, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }] - sign.side_effect = [b'signature'] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") + request=request, target_audience="https://audience.com" + ) # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: - assert token.endswith(b'.c2lnbmF0dXJl') + assert token.endswith(b".c2lnbmF0dXJl") # Check that the credentials have the token and proper expiration assert payload == { - 'aud': 'https://www.googleapis.com/oauth2/v4/token', - 'exp': 3600, - 'iat': 0, - 'iss': 'service-account@example.com', - 'target_audience': 'https://audience.com'} + "aud": "https://www.googleapis.com/oauth2/v4/token", + "exp": 3600, + "iat": 0, + "iss": "service-account@example.com", + "target_audience": "https://audience.com", + } @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_with_service_account(self, sign, get, utcnow): - sign.side_effect = [b'signature'] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com", - service_account_email="service-account@other.com") + request=request, + target_audience="https://audience.com", + service_account_email="service-account@other.com", + ) # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: - assert token.endswith(b'.c2lnbmF0dXJl') + assert token.endswith(b".c2lnbmF0dXJl") # Check that the credentials have the token and proper expiration assert payload == { - 'aud': 'https://www.googleapis.com/oauth2/v4/token', - 'exp': 3600, - 'iat': 0, - 'iss': 'service-account@other.com', - 'target_audience': 'https://audience.com'} + "aud": "https://www.googleapis.com/oauth2/v4/token", + "exp": 3600, + "iat": 0, + "iss": "service-account@other.com", + "target_audience": "https://audience.com", + } @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_additional_claims(self, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }] - sign.side_effect = [b'signature'] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com", - additional_claims={'foo': 'bar'}) + request=request, + target_audience="https://audience.com", + additional_claims={"foo": "bar"}, + ) # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: - assert token.endswith(b'.c2lnbmF0dXJl') + assert token.endswith(b".c2lnbmF0dXJl") # Check that the credentials have the token and proper expiration assert payload == { - 'aud': 'https://www.googleapis.com/oauth2/v4/token', - 'exp': 3600, - 'iat': 0, - 'iss': 'service-account@example.com', - 'target_audience': 'https://audience.com', - 'foo': 'bar'} + "aud": "https://www.googleapis.com/oauth2/v4/token", + "exp": 3600, + "iat": 0, + "iss": "service-account@example.com", + "target_audience": "https://audience.com", + "foo": "bar", + } @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_with_target_audience(self, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }] - sign.side_effect = [b'signature'] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") - self.credentials = ( - self.credentials.with_target_audience("https://actually.not")) + request=request, target_audience="https://audience.com" + ) + self.credentials = self.credentials.with_target_audience("https://actually.not") # Generate authorization grant: token = self.credentials._make_authorization_grant_assertion() payload = jwt.decode(token, verify=False) # The JWT token signature is 'signature' encoded in base 64: - assert token.endswith(b'.c2lnbmF0dXJl') + assert token.endswith(b".c2lnbmF0dXJl") # Check that the credentials have the token and proper expiration assert payload == { - 'aud': 'https://www.googleapis.com/oauth2/v4/token', - 'exp': 3600, - 'iat': 0, - 'iss': 'service-account@example.com', - 'target_audience': 'https://actually.not'} + "aud": "https://www.googleapis.com/oauth2/v4/token", + "exp": 3600, + "iat": 0, + "iss": "service-account@example.com", + "target_audience": "https://actually.not", + } @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) - @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) + @mock.patch("google.oauth2._client.id_token_jwt_grant", autospec=True) def test_refresh_success(self, id_token_jwt_grant, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }] - sign.side_effect = [b'signature'] - id_token_jwt_grant.side_effect = [( - 'idtoken', - datetime.datetime.utcfromtimestamp(3600), - {}, - )] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] + id_token_jwt_grant.side_effect = [ + ("idtoken", datetime.datetime.utcfromtimestamp(3600), {}) + ] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") + request=request, target_audience="https://audience.com" + ) # Refresh credentials self.credentials.refresh(None) # Check that the credentials have the token and proper expiration - assert self.credentials.token == 'idtoken' - assert self.credentials.expiry == ( - datetime.datetime.utcfromtimestamp(3600)) + assert self.credentials.token == "idtoken" + assert self.credentials.expiry == (datetime.datetime.utcfromtimestamp(3600)) # Check the credential info - assert (self.credentials.service_account_email == - 'service-account@example.com') + assert self.credentials.service_account_email == "service-account@example.com" # Check that the credentials are valid (have a token and are not # expired) assert self.credentials.valid @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_refresh_error(self, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'], - }] - sign.side_effect = [b'signature'] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) response = mock.Mock() @@ -314,43 +324,41 @@ def test_refresh_error(self, sign, get, utcnow): request.side_effect = [response] self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") + request=request, target_audience="https://audience.com" + ) with pytest.raises(exceptions.RefreshError) as excinfo: self.credentials.refresh(request) - assert excinfo.match(r'http error') + assert excinfo.match(r"http error") @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.utcfromtimestamp(0)) - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) - @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) - def test_before_request_refreshes( - self, id_token_jwt_grant, sign, get, utcnow): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': 'one two' - }] - sign.side_effect = [b'signature'] - id_token_jwt_grant.side_effect = [( - 'idtoken', - datetime.datetime.utcfromtimestamp(3600), - {}, - )] + "google.auth._helpers.utcnow", + return_value=datetime.datetime.utcfromtimestamp(0), + ) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) + @mock.patch("google.oauth2._client.id_token_jwt_grant", autospec=True) + def test_before_request_refreshes(self, id_token_jwt_grant, sign, get, utcnow): + get.side_effect = [ + {"email": "service-account@example.com", "scopes": "one two"} + ] + sign.side_effect = [b"signature"] + id_token_jwt_grant.side_effect = [ + ("idtoken", datetime.datetime.utcfromtimestamp(3600), {}) + ] request = mock.create_autospec(transport.Request, instance=True) self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") + request=request, target_audience="https://audience.com" + ) # Credentials should start as invalid assert not self.credentials.valid # before_request should cause a refresh request = mock.create_autospec(transport.Request, instance=True) - self.credentials.before_request( - request, 'GET', 'http://example.com?a=1#3', {}) + self.credentials.before_request(request, "GET", "http://example.com?a=1#3", {}) # The refresh endpoint should've been called. assert get.called @@ -358,14 +366,13 @@ def test_before_request_refreshes( # Credentials should now be valid. assert self.credentials.valid - @mock.patch('google.auth.compute_engine._metadata.get', autospec=True) - @mock.patch('google.auth.iam.Signer.sign', autospec=True) + @mock.patch("google.auth.compute_engine._metadata.get", autospec=True) + @mock.patch("google.auth.iam.Signer.sign", autospec=True) def test_sign_bytes(self, sign, get): - get.side_effect = [{ - 'email': 'service-account@example.com', - 'scopes': ['one', 'two'] - }] - sign.side_effect = [b'signature'] + get.side_effect = [ + {"email": "service-account@example.com", "scopes": ["one", "two"]} + ] + sign.side_effect = [b"signature"] request = mock.create_autospec(transport.Request, instance=True) response = mock.Mock() @@ -374,10 +381,11 @@ def test_sign_bytes(self, sign, get): request.side_effect = [response] self.credentials = credentials.IDTokenCredentials( - request=request, target_audience="https://audience.com") + request=request, target_audience="https://audience.com" + ) # Generate authorization grant: signature = self.credentials.sign_bytes(b"some bytes") # The JWT token signature is 'signature' encoded in base 64: - assert signature == b'signature' + assert signature == b"signature" diff --git a/tests/conftest.py b/tests/conftest.py index c9e3f8492..2ccc132e2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,14 +24,14 @@ def mock_non_existent_module(monkeypatch): Additionally mocks any non-existing modules specified in the dotted path. """ + def _mock_non_existent_module(path): - parts = path.split('.') + parts = path.split(".") partial = [] for part in parts: partial.append(part) - current_module = '.'.join(partial) + current_module = ".".join(partial) if current_module not in sys.modules: - monkeypatch.setitem( - sys.modules, current_module, mock.MagicMock()) + monkeypatch.setitem(sys.modules, current_module, mock.MagicMock()) return _mock_non_existent_module diff --git a/tests/crypt/test__cryptography_rsa.py b/tests/crypt/test__cryptography_rsa.py index a7ebb64a7..10f926bc7 100644 --- a/tests/crypt/test__cryptography_rsa.py +++ b/tests/crypt/test__cryptography_rsa.py @@ -23,21 +23,21 @@ from google.auth.crypt import base -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES -with open(os.path.join(DATA_DIR, 'privatekey.pub'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pub"), "rb") as fh: PUBLIC_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: PUBLIC_CERT_BYTES = fh.read() # To generate pem_from_pkcs12.pem and privatekey.p12: @@ -46,22 +46,22 @@ # $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \ # > -out pem_from_pkcs12.pem -with open(os.path.join(DATA_DIR, 'pem_from_pkcs12.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "pem_from_pkcs12.pem"), "rb") as fh: PKCS8_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'privatekey.p12'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.p12"), "rb") as fh: PKCS12_KEY_BYTES = fh.read() # The service account JSON file can be generated from the Google Cloud Console. -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) class TestRSAVerifier(object): def test_verify_success(self): - to_sign = b'foo' + to_sign = b"foo" signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) @@ -69,7 +69,7 @@ def test_verify_success(self): assert verifier.verify(to_sign, actual_signature) def test_verify_unicode_success(self): - to_sign = u'foo' + to_sign = u"foo" signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) @@ -78,10 +78,10 @@ def test_verify_unicode_success(self): def test_verify_failure(self): verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) - bad_signature1 = b'' - assert not verifier.verify(b'foo', bad_signature1) - bad_signature2 = b'a' - assert not verifier.verify(b'foo', bad_signature2) + bad_signature1 = b"" + assert not verifier.verify(b"foo", bad_signature1) + bad_signature2 = b"a" + assert not verifier.verify(b"foo", bad_signature2) def test_from_string_pub_key(self): verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) @@ -134,16 +134,16 @@ def test_from_string_pkcs12(self): _cryptography_rsa.RSASigner.from_string(PKCS12_KEY_BYTES) def test_from_string_bogus_key(self): - key_bytes = 'bogus-key' + key_bytes = "bogus-key" with pytest.raises(ValueError): _cryptography_rsa.RSASigner.from_string(key_bytes) def test_from_service_account_info(self): signer = _cryptography_rsa.RSASigner.from_service_account_info( - SERVICE_ACCOUNT_INFO) + SERVICE_ACCOUNT_INFO + ) - assert signer.key_id == SERVICE_ACCOUNT_INFO[ - base._JSON_FILE_PRIVATE_KEY_ID] + assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.RSAPrivateKey) def test_from_service_account_info_missing_key(self): @@ -154,8 +154,8 @@ def test_from_service_account_info_missing_key(self): def test_from_service_account_file(self): signer = _cryptography_rsa.RSASigner.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE) + SERVICE_ACCOUNT_JSON_FILE + ) - assert signer.key_id == SERVICE_ACCOUNT_INFO[ - base._JSON_FILE_PRIVATE_KEY_ID] + assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.RSAPrivateKey) diff --git a/tests/crypt/test__python_rsa.py b/tests/crypt/test__python_rsa.py index d13105f47..08b9503a2 100644 --- a/tests/crypt/test__python_rsa.py +++ b/tests/crypt/test__python_rsa.py @@ -26,21 +26,21 @@ from google.auth.crypt import base -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() PKCS1_KEY_BYTES = PRIVATE_KEY_BYTES -with open(os.path.join(DATA_DIR, 'privatekey.pub'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pub"), "rb") as fh: PUBLIC_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: PUBLIC_CERT_BYTES = fh.read() # To generate pem_from_pkcs12.pem and privatekey.p12: @@ -49,22 +49,22 @@ # $ openssl pkcs12 -in privatekey.p12 -nocerts -nodes \ # > -out pem_from_pkcs12.pem -with open(os.path.join(DATA_DIR, 'pem_from_pkcs12.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "pem_from_pkcs12.pem"), "rb") as fh: PKCS8_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'privatekey.p12'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.p12"), "rb") as fh: PKCS12_KEY_BYTES = fh.read() # The service account JSON file can be generated from the Google Cloud Console. -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) class TestRSAVerifier(object): def test_verify_success(self): - to_sign = b'foo' + to_sign = b"foo" signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) @@ -72,7 +72,7 @@ def test_verify_success(self): assert verifier.verify(to_sign, actual_signature) def test_verify_unicode_success(self): - to_sign = u'foo' + to_sign = u"foo" signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES) actual_signature = signer.sign(to_sign) @@ -81,10 +81,10 @@ def test_verify_unicode_success(self): def test_verify_failure(self): verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) - bad_signature1 = b'' - assert not verifier.verify(b'foo', bad_signature1) - bad_signature2 = b'a' - assert not verifier.verify(b'foo', bad_signature2) + bad_signature1 = b"" + assert not verifier.verify(b"foo", bad_signature1) + bad_signature2 = b"a" + assert not verifier.verify(b"foo", bad_signature2) def test_from_string_pub_key(self): verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES) @@ -110,15 +110,15 @@ def test_from_string_pub_cert_unicode(self): def test_from_string_pub_cert_failure(self): cert_bytes = PUBLIC_CERT_BYTES - true_der = rsa.pem.load_pem(cert_bytes, 'CERTIFICATE') + true_der = rsa.pem.load_pem(cert_bytes, "CERTIFICATE") load_pem_patch = mock.patch( - 'rsa.pem.load_pem', return_value=true_der + b'extra', - autospec=True) + "rsa.pem.load_pem", return_value=true_der + b"extra", autospec=True + ) with load_pem_patch as load_pem: with pytest.raises(ValueError): _python_rsa.RSAVerifier.from_string(cert_bytes) - load_pem.assert_called_once_with(cert_bytes, 'CERTIFICATE') + load_pem.assert_called_once_with(cert_bytes, "CERTIFICATE") class TestRSASigner(object): @@ -141,21 +141,21 @@ def test_from_string_pkcs8(self): def test_from_string_pkcs8_extra_bytes(self): key_bytes = PKCS8_KEY_BYTES _, pem_bytes = pem.readPemBlocksFromFile( - six.StringIO(_helpers.from_bytes(key_bytes)), - _python_rsa._PKCS8_MARKER) + six.StringIO(_helpers.from_bytes(key_bytes)), _python_rsa._PKCS8_MARKER + ) - key_info, remaining = None, 'extra' + key_info, remaining = None, "extra" decode_patch = mock.patch( - 'pyasn1.codec.der.decoder.decode', + "pyasn1.codec.der.decoder.decode", return_value=(key_info, remaining), - autospec=True) + autospec=True, + ) with decode_patch as decode: with pytest.raises(ValueError): _python_rsa.RSASigner.from_string(key_bytes) # Verify mock was called. - decode.assert_called_once_with( - pem_bytes, asn1Spec=_python_rsa._PKCS8_SPEC) + decode.assert_called_once_with(pem_bytes, asn1Spec=_python_rsa._PKCS8_SPEC) def test_from_string_pkcs8_unicode(self): key_bytes = _helpers.from_bytes(PKCS8_KEY_BYTES) @@ -168,16 +168,14 @@ def test_from_string_pkcs12(self): _python_rsa.RSASigner.from_string(PKCS12_KEY_BYTES) def test_from_string_bogus_key(self): - key_bytes = 'bogus-key' + key_bytes = "bogus-key" with pytest.raises(ValueError): _python_rsa.RSASigner.from_string(key_bytes) def test_from_service_account_info(self): - signer = _python_rsa.RSASigner.from_service_account_info( - SERVICE_ACCOUNT_INFO) + signer = _python_rsa.RSASigner.from_service_account_info(SERVICE_ACCOUNT_INFO) - assert signer.key_id == SERVICE_ACCOUNT_INFO[ - base._JSON_FILE_PRIVATE_KEY_ID] + assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.key.PrivateKey) def test_from_service_account_info_missing_key(self): @@ -188,8 +186,8 @@ def test_from_service_account_info_missing_key(self): def test_from_service_account_file(self): signer = _python_rsa.RSASigner.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE) + SERVICE_ACCOUNT_JSON_FILE + ) - assert signer.key_id == SERVICE_ACCOUNT_INFO[ - base._JSON_FILE_PRIVATE_KEY_ID] + assert signer.key_id == SERVICE_ACCOUNT_INFO[base._JSON_FILE_PRIVATE_KEY_ID] assert isinstance(signer._key, rsa.key.PrivateKey) diff --git a/tests/crypt/test_crypt.py b/tests/crypt/test_crypt.py index d8b1d00a8..16ff2e0bd 100644 --- a/tests/crypt/test_crypt.py +++ b/tests/crypt/test_crypt.py @@ -17,43 +17,42 @@ from google.auth import crypt -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") # To generate privatekey.pem, privatekey.pub, and public_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out public_cert.pem \ # > -keyout privatekey.pem # $ openssl rsa -in privatekey.pem -pubout -out privatekey.pub -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: PUBLIC_CERT_BYTES = fh.read() # To generate other_cert.pem: # $ openssl req -new -newkey rsa:1024 -x509 -nodes -out other_cert.pem -with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "other_cert.pem"), "rb") as fh: OTHER_CERT_BYTES = fh.read() def test_verify_signature(): - to_sign = b'foo' + to_sign = b"foo" signer = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES) signature = signer.sign(to_sign) - assert crypt.verify_signature( - to_sign, signature, PUBLIC_CERT_BYTES) + assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) # List of certs assert crypt.verify_signature( - to_sign, signature, [OTHER_CERT_BYTES, PUBLIC_CERT_BYTES]) + to_sign, signature, [OTHER_CERT_BYTES, PUBLIC_CERT_BYTES] + ) def test_verify_signature_failure(): - to_sign = b'foo' + to_sign = b"foo" signer = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES) signature = signer.sign(to_sign) - assert not crypt.verify_signature( - to_sign, signature, OTHER_CERT_BYTES) + assert not crypt.verify_signature(to_sign, signature, OTHER_CERT_BYTES) diff --git a/tests/oauth2/test__client.py b/tests/oauth2/test__client.py index 6fc4c3b12..c415a1f44 100644 --- a/tests/oauth2/test__client.py +++ b/tests/oauth2/test__client.py @@ -30,42 +30,44 @@ from google.oauth2 import _client -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() -SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') +SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1") -SCOPES_AS_LIST = ['https://www.googleapis.com/auth/pubsub', - 'https://www.googleapis.com/auth/logging.write'] -SCOPES_AS_STRING = ('https://www.googleapis.com/auth/pubsub' - ' https://www.googleapis.com/auth/logging.write') +SCOPES_AS_LIST = [ + "https://www.googleapis.com/auth/pubsub", + "https://www.googleapis.com/auth/logging.write", +] +SCOPES_AS_STRING = ( + "https://www.googleapis.com/auth/pubsub" + " https://www.googleapis.com/auth/logging.write" +) def test__handle_error_response(): - response_data = json.dumps({ - 'error': 'help', - 'error_description': 'I\'m alive'}) + response_data = json.dumps({"error": "help", "error_description": "I'm alive"}) with pytest.raises(exceptions.RefreshError) as excinfo: _client._handle_error_response(response_data) - assert excinfo.match(r'help: I\'m alive') + assert excinfo.match(r"help: I\'m alive") def test__handle_error_response_non_json(): - response_data = 'Help, I\'m alive' + response_data = "Help, I'm alive" with pytest.raises(exceptions.RefreshError) as excinfo: _client._handle_error_response(response_data) - assert excinfo.match(r'Help, I\'m alive') + assert excinfo.match(r"Help, I\'m alive") -@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) +@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test__parse_expiry(unused_utcnow): - result = _client._parse_expiry({'expires_in': 500}) + result = _client._parse_expiry({"expires_in": 500}) assert result == datetime.datetime.min + datetime.timedelta(seconds=500) @@ -76,188 +78,214 @@ def test__parse_expiry_none(): def make_request(response_data, status=http_client.OK): response = mock.create_autospec(transport.Response, instance=True) response.status = status - response.data = json.dumps(response_data).encode('utf-8') + response.data = json.dumps(response_data).encode("utf-8") request = mock.create_autospec(transport.Request) request.return_value = response return request def test__token_endpoint_request(): - request = make_request({'test': 'response'}) + request = make_request({"test": "response"}) result = _client._token_endpoint_request( - request, 'http://example.com', {'test': 'params'}) + request, "http://example.com", {"test": "params"} + ) # Check request call request.assert_called_with( - method='POST', - url='http://example.com', - headers={'content-type': 'application/x-www-form-urlencoded'}, - body='test=params') + method="POST", + url="http://example.com", + headers={"content-type": "application/x-www-form-urlencoded"}, + body="test=params", + ) # Check result - assert result == {'test': 'response'} + assert result == {"test": "response"} def test__token_endpoint_request_error(): request = make_request({}, status=http_client.BAD_REQUEST) with pytest.raises(exceptions.RefreshError): - _client._token_endpoint_request(request, 'http://example.com', {}) + _client._token_endpoint_request(request, "http://example.com", {}) def test__token_endpoint_request_internal_failure_error(): - request = make_request({'error': 'internal_failure', - 'error_description': 'internal_failure'}, - status=http_client.BAD_REQUEST) + request = make_request( + {"error": "internal_failure", "error_description": "internal_failure"}, + status=http_client.BAD_REQUEST, + ) with pytest.raises(exceptions.RefreshError): _client._token_endpoint_request( - request, 'http://example.com', - {'error': 'internal_failure', - 'error_description': 'internal_failure'}) + request, + "http://example.com", + {"error": "internal_failure", "error_description": "internal_failure"}, + ) def verify_request_params(request, params): - request_body = request.call_args[1]['body'] + request_body = request.call_args[1]["body"] request_params = urllib.parse.parse_qs(request_body) for key, value in six.iteritems(params): assert request_params[key][0] == value -@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) +@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test_jwt_grant(utcnow): - request = make_request({ - 'access_token': 'token', - 'expires_in': 500, - 'extra': 'data'}) + request = make_request( + {"access_token": "token", "expires_in": 500, "extra": "data"} + ) token, expiry, extra_data = _client.jwt_grant( - request, 'http://example.com', 'assertion_value') + request, "http://example.com", "assertion_value" + ) # Check request call - verify_request_params(request, { - 'grant_type': _client._JWT_GRANT_TYPE, - 'assertion': 'assertion_value' - }) + verify_request_params( + request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"} + ) # Check result - assert token == 'token' + assert token == "token" assert expiry == utcnow() + datetime.timedelta(seconds=500) - assert extra_data['extra'] == 'data' + assert extra_data["extra"] == "data" def test_jwt_grant_no_access_token(): - request = make_request({ - # No access token. - 'expires_in': 500, - 'extra': 'data'}) + request = make_request( + { + # No access token. + "expires_in": 500, + "extra": "data", + } + ) with pytest.raises(exceptions.RefreshError): - _client.jwt_grant(request, 'http://example.com', 'assertion_value') + _client.jwt_grant(request, "http://example.com", "assertion_value") def test_id_token_jwt_grant(): now = _helpers.utcnow() id_token_expiry = _helpers.datetime_to_secs(now) - id_token = jwt.encode(SIGNER, {'exp': id_token_expiry}).decode('utf-8') - request = make_request({ - 'id_token': id_token, - 'extra': 'data'}) + id_token = jwt.encode(SIGNER, {"exp": id_token_expiry}).decode("utf-8") + request = make_request({"id_token": id_token, "extra": "data"}) token, expiry, extra_data = _client.id_token_jwt_grant( - request, 'http://example.com', 'assertion_value') + request, "http://example.com", "assertion_value" + ) # Check request call - verify_request_params(request, { - 'grant_type': _client._JWT_GRANT_TYPE, - 'assertion': 'assertion_value' - }) + verify_request_params( + request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"} + ) # Check result assert token == id_token # JWT does not store microseconds now = now.replace(microsecond=0) assert expiry == now - assert extra_data['extra'] == 'data' + assert extra_data["extra"] == "data" def test_id_token_jwt_grant_no_access_token(): - request = make_request({ - # No access token. - 'expires_in': 500, - 'extra': 'data'}) + request = make_request( + { + # No access token. + "expires_in": 500, + "extra": "data", + } + ) with pytest.raises(exceptions.RefreshError): - _client.id_token_jwt_grant( - request, 'http://example.com', 'assertion_value') + _client.id_token_jwt_grant(request, "http://example.com", "assertion_value") -@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) +@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test_refresh_grant(unused_utcnow): - request = make_request({ - 'access_token': 'token', - 'refresh_token': 'new_refresh_token', - 'expires_in': 500, - 'extra': 'data'}) + request = make_request( + { + "access_token": "token", + "refresh_token": "new_refresh_token", + "expires_in": 500, + "extra": "data", + } + ) token, refresh_token, expiry, extra_data = _client.refresh_grant( - request, 'http://example.com', 'refresh_token', 'client_id', - 'client_secret') + request, "http://example.com", "refresh_token", "client_id", "client_secret" + ) # Check request call - verify_request_params(request, { - 'grant_type': _client._REFRESH_GRANT_TYPE, - 'refresh_token': 'refresh_token', - 'client_id': 'client_id', - 'client_secret': 'client_secret' - }) + verify_request_params( + request, + { + "grant_type": _client._REFRESH_GRANT_TYPE, + "refresh_token": "refresh_token", + "client_id": "client_id", + "client_secret": "client_secret", + }, + ) # Check result - assert token == 'token' - assert refresh_token == 'new_refresh_token' + assert token == "token" + assert refresh_token == "new_refresh_token" assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500) - assert extra_data['extra'] == 'data' + assert extra_data["extra"] == "data" -@mock.patch('google.auth._helpers.utcnow', return_value=datetime.datetime.min) +@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test_refresh_grant_with_scopes(unused_utcnow): - request = make_request({ - 'access_token': 'token', - 'refresh_token': 'new_refresh_token', - 'expires_in': 500, - 'extra': 'data', - 'scope': SCOPES_AS_STRING}) + request = make_request( + { + "access_token": "token", + "refresh_token": "new_refresh_token", + "expires_in": 500, + "extra": "data", + "scope": SCOPES_AS_STRING, + } + ) token, refresh_token, expiry, extra_data = _client.refresh_grant( - request, 'http://example.com', 'refresh_token', 'client_id', - 'client_secret', SCOPES_AS_LIST) + request, + "http://example.com", + "refresh_token", + "client_id", + "client_secret", + SCOPES_AS_LIST, + ) # Check request call. - verify_request_params(request, { - 'grant_type': _client._REFRESH_GRANT_TYPE, - 'refresh_token': 'refresh_token', - 'client_id': 'client_id', - 'client_secret': 'client_secret', - 'scope': SCOPES_AS_STRING - }) + verify_request_params( + request, + { + "grant_type": _client._REFRESH_GRANT_TYPE, + "refresh_token": "refresh_token", + "client_id": "client_id", + "client_secret": "client_secret", + "scope": SCOPES_AS_STRING, + }, + ) # Check result. - assert token == 'token' - assert refresh_token == 'new_refresh_token' + assert token == "token" + assert refresh_token == "new_refresh_token" assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500) - assert extra_data['extra'] == 'data' + assert extra_data["extra"] == "data" def test_refresh_grant_no_access_token(): - request = make_request({ - # No access token. - 'refresh_token': 'new_refresh_token', - 'expires_in': 500, - 'extra': 'data'}) + request = make_request( + { + # No access token. + "refresh_token": "new_refresh_token", + "expires_in": 500, + "extra": "data", + } + ) with pytest.raises(exceptions.RefreshError): _client.refresh_grant( - request, 'http://example.com', 'refresh_token', 'client_id', - 'client_secret') + request, "http://example.com", "refresh_token", "client_id", "client_secret" + ) diff --git a/tests/oauth2/test_credentials.py b/tests/oauth2/test_credentials.py index 32315096a..8bfdd7e0a 100644 --- a/tests/oauth2/test_credentials.py +++ b/tests/oauth2/test_credentials.py @@ -25,26 +25,29 @@ from google.oauth2 import credentials -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") -AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, 'authorized_user.json') +AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, "authorized_user.json") -with open(AUTH_USER_JSON_FILE, 'r') as fh: +with open(AUTH_USER_JSON_FILE, "r") as fh: AUTH_USER_INFO = json.load(fh) class TestCredentials(object): - TOKEN_URI = 'https://example.com/oauth2/token' - REFRESH_TOKEN = 'refresh_token' - CLIENT_ID = 'client_id' - CLIENT_SECRET = 'client_secret' + TOKEN_URI = "https://example.com/oauth2/token" + REFRESH_TOKEN = "refresh_token" + CLIENT_ID = "client_id" + CLIENT_SECRET = "client_secret" @classmethod def make_credentials(cls): return credentials.Credentials( - token=None, refresh_token=cls.REFRESH_TOKEN, - token_uri=cls.TOKEN_URI, client_id=cls.CLIENT_ID, - client_secret=cls.CLIENT_SECRET) + token=None, + refresh_token=cls.REFRESH_TOKEN, + token_uri=cls.TOKEN_URI, + client_id=cls.CLIENT_ID, + client_secret=cls.CLIENT_SECRET, + ) def test_default_state(self): credentials = self.make_credentials() @@ -59,14 +62,15 @@ def test_default_state(self): assert credentials.client_id == self.CLIENT_ID assert credentials.client_secret == self.CLIENT_SECRET - @mock.patch('google.oauth2._client.refresh_grant', autospec=True) + @mock.patch("google.oauth2._client.refresh_grant", autospec=True) @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.min + _helpers.CLOCK_SKEW, + ) def test_refresh_success(self, unused_utcnow, refresh_grant): - token = 'token' + token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) - grant_response = {'id_token': mock.sentinel.id_token} + grant_response = {"id_token": mock.sentinel.id_token} refresh_grant.return_value = ( # Access token token, @@ -75,7 +79,8 @@ def test_refresh_success(self, unused_utcnow, refresh_grant): # Expiry, expiry, # Extra data - grant_response) + grant_response, + ) request = mock.create_autospec(transport.Request) credentials = self.make_credentials() @@ -85,8 +90,13 @@ def test_refresh_success(self, unused_utcnow, refresh_grant): # Check jwt grant call. refresh_grant.assert_called_with( - request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, - self.CLIENT_SECRET, None) + request, + self.TOKEN_URI, + self.REFRESH_TOKEN, + self.CLIENT_ID, + self.CLIENT_SECRET, + None, + ) # Check that the credentials have the token and expiry assert credentials.token == token @@ -99,24 +109,25 @@ def test_refresh_success(self, unused_utcnow, refresh_grant): def test_refresh_no_refresh_token(self): request = mock.create_autospec(transport.Request) - credentials_ = credentials.Credentials( - token=None, refresh_token=None) + credentials_ = credentials.Credentials(token=None, refresh_token=None) - with pytest.raises(exceptions.RefreshError, match='necessary fields'): + with pytest.raises(exceptions.RefreshError, match="necessary fields"): credentials_.refresh(request) request.assert_not_called() - @mock.patch('google.oauth2._client.refresh_grant', autospec=True) + @mock.patch("google.oauth2._client.refresh_grant", autospec=True) @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.min + _helpers.CLOCK_SKEW, + ) def test_credentials_with_scopes_requested_refresh_success( - self, unused_utcnow, refresh_grant): - scopes = ['email', 'profile'] - token = 'token' + self, unused_utcnow, refresh_grant + ): + scopes = ["email", "profile"] + token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) - grant_response = {'id_token': mock.sentinel.id_token} + grant_response = {"id_token": mock.sentinel.id_token} refresh_grant.return_value = ( # Access token token, @@ -125,21 +136,31 @@ def test_credentials_with_scopes_requested_refresh_success( # Expiry, expiry, # Extra data - grant_response) + grant_response, + ) request = mock.create_autospec(transport.Request) creds = credentials.Credentials( - token=None, refresh_token=self.REFRESH_TOKEN, - token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID, - client_secret=self.CLIENT_SECRET, scopes=scopes) + token=None, + refresh_token=self.REFRESH_TOKEN, + token_uri=self.TOKEN_URI, + client_id=self.CLIENT_ID, + client_secret=self.CLIENT_SECRET, + scopes=scopes, + ) # Refresh credentials creds.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( - request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, - self.CLIENT_SECRET, scopes) + request, + self.TOKEN_URI, + self.REFRESH_TOKEN, + self.CLIENT_ID, + self.CLIENT_SECRET, + scopes, + ) # Check that the credentials have the token and expiry assert creds.token == token @@ -151,17 +172,21 @@ def test_credentials_with_scopes_requested_refresh_success( # expired.) assert creds.valid - @mock.patch('google.oauth2._client.refresh_grant', autospec=True) + @mock.patch("google.oauth2._client.refresh_grant", autospec=True) @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.min + _helpers.CLOCK_SKEW, + ) def test_credentials_with_scopes_returned_refresh_success( - self, unused_utcnow, refresh_grant): - scopes = ['email', 'profile'] - token = 'token' + self, unused_utcnow, refresh_grant + ): + scopes = ["email", "profile"] + token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) - grant_response = {'id_token': mock.sentinel.id_token, - 'scopes': ' '.join(scopes)} + grant_response = { + "id_token": mock.sentinel.id_token, + "scopes": " ".join(scopes), + } refresh_grant.return_value = ( # Access token token, @@ -170,21 +195,31 @@ def test_credentials_with_scopes_returned_refresh_success( # Expiry, expiry, # Extra data - grant_response) + grant_response, + ) request = mock.create_autospec(transport.Request) creds = credentials.Credentials( - token=None, refresh_token=self.REFRESH_TOKEN, - token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID, - client_secret=self.CLIENT_SECRET, scopes=scopes) + token=None, + refresh_token=self.REFRESH_TOKEN, + token_uri=self.TOKEN_URI, + client_id=self.CLIENT_ID, + client_secret=self.CLIENT_SECRET, + scopes=scopes, + ) # Refresh credentials creds.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( - request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, - self.CLIENT_SECRET, scopes) + request, + self.TOKEN_URI, + self.REFRESH_TOKEN, + self.CLIENT_ID, + self.CLIENT_SECRET, + scopes, + ) # Check that the credentials have the token and expiry assert creds.token == token @@ -196,18 +231,22 @@ def test_credentials_with_scopes_returned_refresh_success( # expired.) assert creds.valid - @mock.patch('google.oauth2._client.refresh_grant', autospec=True) + @mock.patch("google.oauth2._client.refresh_grant", autospec=True) @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min + _helpers.CLOCK_SKEW) + "google.auth._helpers.utcnow", + return_value=datetime.datetime.min + _helpers.CLOCK_SKEW, + ) def test_credentials_with_scopes_refresh_failure_raises_refresh_error( - self, unused_utcnow, refresh_grant): - scopes = ['email', 'profile'] - scopes_returned = ['email'] - token = 'token' + self, unused_utcnow, refresh_grant + ): + scopes = ["email", "profile"] + scopes_returned = ["email"] + token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) - grant_response = {'id_token': mock.sentinel.id_token, - 'scopes': ' '.join(scopes_returned)} + grant_response = { + "id_token": mock.sentinel.id_token, + "scopes": " ".join(scopes_returned), + } refresh_grant.return_value = ( # Access token token, @@ -216,23 +255,34 @@ def test_credentials_with_scopes_refresh_failure_raises_refresh_error( # Expiry, expiry, # Extra data - grant_response) + grant_response, + ) request = mock.create_autospec(transport.Request) creds = credentials.Credentials( - token=None, refresh_token=self.REFRESH_TOKEN, - token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID, - client_secret=self.CLIENT_SECRET, scopes=scopes) + token=None, + refresh_token=self.REFRESH_TOKEN, + token_uri=self.TOKEN_URI, + client_id=self.CLIENT_ID, + client_secret=self.CLIENT_SECRET, + scopes=scopes, + ) # Refresh credentials - with pytest.raises(exceptions.RefreshError, - match='Not all requested scopes were granted'): + with pytest.raises( + exceptions.RefreshError, match="Not all requested scopes were granted" + ): creds.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( - request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, - self.CLIENT_SECRET, scopes) + request, + self.TOKEN_URI, + self.REFRESH_TOKEN, + self.CLIENT_ID, + self.CLIENT_SECRET, + scopes, + ) # Check that the credentials have the token and expiry assert creds.token == token @@ -248,37 +298,36 @@ def test_from_authorized_user_info(self): info = AUTH_USER_INFO.copy() creds = credentials.Credentials.from_authorized_user_info(info) - assert creds.client_secret == info['client_secret'] - assert creds.client_id == info['client_id'] - assert creds.refresh_token == info['refresh_token'] + assert creds.client_secret == info["client_secret"] + assert creds.client_id == info["client_id"] + assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None - scopes = ['email', 'profile'] - creds = credentials.Credentials.from_authorized_user_info( - info, scopes) - assert creds.client_secret == info['client_secret'] - assert creds.client_id == info['client_id'] - assert creds.refresh_token == info['refresh_token'] + scopes = ["email", "profile"] + creds = credentials.Credentials.from_authorized_user_info(info, scopes) + assert creds.client_secret == info["client_secret"] + assert creds.client_id == info["client_id"] + assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes def test_from_authorized_user_file(self): info = AUTH_USER_INFO.copy() - creds = credentials.Credentials.from_authorized_user_file( - AUTH_USER_JSON_FILE) - assert creds.client_secret == info['client_secret'] - assert creds.client_id == info['client_id'] - assert creds.refresh_token == info['refresh_token'] + creds = credentials.Credentials.from_authorized_user_file(AUTH_USER_JSON_FILE) + assert creds.client_secret == info["client_secret"] + assert creds.client_id == info["client_id"] + assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None - scopes = ['email', 'profile'] + scopes = ["email", "profile"] creds = credentials.Credentials.from_authorized_user_file( - AUTH_USER_JSON_FILE, scopes) - assert creds.client_secret == info['client_secret'] - assert creds.client_id == info['client_id'] - assert creds.refresh_token == info['refresh_token'] + AUTH_USER_JSON_FILE, scopes + ) + assert creds.client_secret == info["client_secret"] + assert creds.client_id == info["client_id"] + assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes diff --git a/tests/oauth2/test_id_token.py b/tests/oauth2/test_id_token.py index 360f92f53..980a8e982 100644 --- a/tests/oauth2/test_id_token.py +++ b/tests/oauth2/test_id_token.py @@ -27,7 +27,7 @@ def make_request(status, data=None): response.status = status if data is not None: - response.data = json.dumps(data).encode('utf-8') + response.data = json.dumps(data).encode("utf-8") request = mock.create_autospec(transport.Request) request.return_value = response @@ -35,12 +35,12 @@ def make_request(status, data=None): def test__fetch_certs_success(): - certs = {'1': 'cert'} + certs = {"1": "cert"} request = make_request(200, certs) returned_certs = id_token._fetch_certs(request, mock.sentinel.cert_url) - request.assert_called_once_with(mock.sentinel.cert_url, method='GET') + request.assert_called_once_with(mock.sentinel.cert_url, method="GET") assert returned_certs == certs @@ -50,66 +50,67 @@ def test__fetch_certs_failure(): with pytest.raises(exceptions.TransportError): id_token._fetch_certs(request, mock.sentinel.cert_url) - request.assert_called_once_with(mock.sentinel.cert_url, method='GET') + request.assert_called_once_with(mock.sentinel.cert_url, method="GET") -@mock.patch('google.auth.jwt.decode', autospec=True) -@mock.patch('google.oauth2.id_token._fetch_certs', autospec=True) +@mock.patch("google.auth.jwt.decode", autospec=True) +@mock.patch("google.oauth2.id_token._fetch_certs", autospec=True) def test_verify_token(_fetch_certs, decode): result = id_token.verify_token(mock.sentinel.token, mock.sentinel.request) assert result == decode.return_value _fetch_certs.assert_called_once_with( - mock.sentinel.request, id_token._GOOGLE_OAUTH2_CERTS_URL) + mock.sentinel.request, id_token._GOOGLE_OAUTH2_CERTS_URL + ) decode.assert_called_once_with( - mock.sentinel.token, - certs=_fetch_certs.return_value, - audience=None) + mock.sentinel.token, certs=_fetch_certs.return_value, audience=None + ) -@mock.patch('google.auth.jwt.decode', autospec=True) -@mock.patch('google.oauth2.id_token._fetch_certs', autospec=True) +@mock.patch("google.auth.jwt.decode", autospec=True) +@mock.patch("google.oauth2.id_token._fetch_certs", autospec=True) def test_verify_token_args(_fetch_certs, decode): result = id_token.verify_token( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, - certs_url=mock.sentinel.certs_url) + certs_url=mock.sentinel.certs_url, + ) assert result == decode.return_value - _fetch_certs.assert_called_once_with( - mock.sentinel.request, mock.sentinel.certs_url) + _fetch_certs.assert_called_once_with(mock.sentinel.request, mock.sentinel.certs_url) decode.assert_called_once_with( mock.sentinel.token, certs=_fetch_certs.return_value, - audience=mock.sentinel.audience) + audience=mock.sentinel.audience, + ) -@mock.patch('google.oauth2.id_token.verify_token', autospec=True) +@mock.patch("google.oauth2.id_token.verify_token", autospec=True) def test_verify_oauth2_token(verify_token): result = id_token.verify_oauth2_token( - mock.sentinel.token, - mock.sentinel.request, - audience=mock.sentinel.audience) + mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience + ) assert result == verify_token.return_value verify_token.assert_called_once_with( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, - certs_url=id_token._GOOGLE_OAUTH2_CERTS_URL) + certs_url=id_token._GOOGLE_OAUTH2_CERTS_URL, + ) -@mock.patch('google.oauth2.id_token.verify_token', autospec=True) +@mock.patch("google.oauth2.id_token.verify_token", autospec=True) def test_verify_firebase_token(verify_token): result = id_token.verify_firebase_token( - mock.sentinel.token, - mock.sentinel.request, - audience=mock.sentinel.audience) + mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience + ) assert result == verify_token.return_value verify_token.assert_called_once_with( mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, - certs_url=id_token._GOOGLE_APIS_CERTS_URL) + certs_url=id_token._GOOGLE_APIS_CERTS_URL, + ) diff --git a/tests/oauth2/test_service_account.py b/tests/oauth2/test_service_account.py index 54ac0f5e9..0f9d4600c 100644 --- a/tests/oauth2/test_service_account.py +++ b/tests/oauth2/test_service_account.py @@ -25,58 +25,58 @@ from google.oauth2 import service_account -DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: PUBLIC_CERT_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "other_cert.pem"), "rb") as fh: OTHER_CERT_BYTES = fh.read() -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) -SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') +SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1") class TestCredentials(object): - SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' - TOKEN_URI = 'https://example.com/oauth2/token' + SERVICE_ACCOUNT_EMAIL = "service-account@example.com" + TOKEN_URI = "https://example.com/oauth2/token" @classmethod def make_credentials(cls): return service_account.Credentials( - SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI) + SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI + ) def test_from_service_account_info(self): credentials = service_account.Credentials.from_service_account_info( - SERVICE_ACCOUNT_INFO) + SERVICE_ACCOUNT_INFO + ) - assert (credentials._signer.key_id == - SERVICE_ACCOUNT_INFO['private_key_id']) - assert (credentials.service_account_email == - SERVICE_ACCOUNT_INFO['client_email']) - assert credentials._token_uri == SERVICE_ACCOUNT_INFO['token_uri'] + assert credentials._signer.key_id == SERVICE_ACCOUNT_INFO["private_key_id"] + assert credentials.service_account_email == SERVICE_ACCOUNT_INFO["client_email"] + assert credentials._token_uri == SERVICE_ACCOUNT_INFO["token_uri"] def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() - scopes = ['email', 'profile'] - subject = 'subject' - additional_claims = {'meta': 'data'} + scopes = ["email", "profile"] + subject = "subject" + additional_claims = {"meta": "data"} credentials = service_account.Credentials.from_service_account_info( - info, scopes=scopes, subject=subject, - additional_claims=additional_claims) + info, scopes=scopes, subject=subject, additional_claims=additional_claims + ) - assert credentials.service_account_email == info['client_email'] - assert credentials.project_id == info['project_id'] - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._token_uri == info['token_uri'] + assert credentials.service_account_email == info["client_email"] + assert credentials.project_id == info["project_id"] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._token_uri == info["token_uri"] assert credentials._scopes == scopes assert credentials._subject == subject assert credentials._additional_claims == additional_claims @@ -85,27 +85,31 @@ def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = service_account.Credentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE) + SERVICE_ACCOUNT_JSON_FILE + ) - assert credentials.service_account_email == info['client_email'] - assert credentials.project_id == info['project_id'] - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._token_uri == info['token_uri'] + assert credentials.service_account_email == info["client_email"] + assert credentials.project_id == info["project_id"] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._token_uri == info["token_uri"] def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() - scopes = ['email', 'profile'] - subject = 'subject' - additional_claims = {'meta': 'data'} + scopes = ["email", "profile"] + subject = "subject" + additional_claims = {"meta": "data"} credentials = service_account.Credentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE, subject=subject, - scopes=scopes, additional_claims=additional_claims) - - assert credentials.service_account_email == info['client_email'] - assert credentials.project_id == info['project_id'] - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._token_uri == info['token_uri'] + SERVICE_ACCOUNT_JSON_FILE, + subject=subject, + scopes=scopes, + additional_claims=additional_claims, + ) + + assert credentials.service_account_email == info["client_email"] + assert credentials.project_id == info["project_id"] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._token_uri == info["token_uri"] assert credentials._scopes == scopes assert credentials._subject == subject assert credentials._additional_claims == additional_claims @@ -120,7 +124,7 @@ def test_default_state(self): def test_sign_bytes(self): credentials = self.make_credentials() - to_sign = b'123' + to_sign = b"123" signature = credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) @@ -134,46 +138,47 @@ def test_signer_email(self): def test_create_scoped(self): credentials = self.make_credentials() - scopes = ['email', 'profile'] + scopes = ["email", "profile"] credentials = credentials.with_scopes(scopes) assert credentials._scopes == scopes def test_with_claims(self): credentials = self.make_credentials() - new_credentials = credentials.with_claims({'meep': 'moop'}) - assert new_credentials._additional_claims == {'meep': 'moop'} + new_credentials = credentials.with_claims({"meep": "moop"}) + assert new_credentials._additional_claims == {"meep": "moop"} def test__make_authorization_grant_assertion(self): credentials = self.make_credentials() token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL - assert payload['aud'] == self.TOKEN_URI + assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL + assert payload["aud"] == self.TOKEN_URI def test__make_authorization_grant_assertion_scoped(self): credentials = self.make_credentials() - scopes = ['email', 'profile'] + scopes = ["email", "profile"] credentials = credentials.with_scopes(scopes) token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['scope'] == 'email profile' + assert payload["scope"] == "email profile" def test__make_authorization_grant_assertion_subject(self): credentials = self.make_credentials() - subject = 'user@example.com' + subject = "user@example.com" credentials = credentials.with_subject(subject) token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['sub'] == subject + assert payload["sub"] == subject - @mock.patch('google.oauth2._client.jwt_grant', autospec=True) + @mock.patch("google.oauth2._client.jwt_grant", autospec=True) def test_refresh_success(self, jwt_grant): credentials = self.make_credentials() - token = 'token' + token = "token" jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), - {}) + {}, + ) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials @@ -196,20 +201,22 @@ def test_refresh_success(self, jwt_grant): # expired) assert credentials.valid - @mock.patch('google.oauth2._client.jwt_grant', autospec=True) + @mock.patch("google.oauth2._client.jwt_grant", autospec=True) def test_before_request_refreshes(self, jwt_grant): credentials = self.make_credentials() - token = 'token' + token = "token" jwt_grant.return_value = ( - token, _helpers.utcnow() + datetime.timedelta(seconds=500), None) + token, + _helpers.utcnow() + datetime.timedelta(seconds=500), + None, + ) request = mock.create_autospec(transport.Request, instance=True) # Credentials should start as invalid assert not credentials.valid # before_request should cause a refresh - credentials.before_request( - request, 'GET', 'http://example.com?a=1#3', {}) + credentials.before_request(request, "GET", "http://example.com?a=1#3", {}) # The refresh endpoint should've been called. assert jwt_grant.called @@ -219,40 +226,36 @@ def test_before_request_refreshes(self, jwt_grant): class TestIDTokenCredentials(object): - SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' - TOKEN_URI = 'https://example.com/oauth2/token' - TARGET_AUDIENCE = 'https://example.com' + SERVICE_ACCOUNT_EMAIL = "service-account@example.com" + TOKEN_URI = "https://example.com/oauth2/token" + TARGET_AUDIENCE = "https://example.com" @classmethod def make_credentials(cls): return service_account.IDTokenCredentials( - SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI, - cls.TARGET_AUDIENCE) + SIGNER, cls.SERVICE_ACCOUNT_EMAIL, cls.TOKEN_URI, cls.TARGET_AUDIENCE + ) def test_from_service_account_info(self): - credentials = ( - service_account.IDTokenCredentials.from_service_account_info( - SERVICE_ACCOUNT_INFO, - target_audience=self.TARGET_AUDIENCE)) - - assert (credentials._signer.key_id == - SERVICE_ACCOUNT_INFO['private_key_id']) - assert (credentials.service_account_email == - SERVICE_ACCOUNT_INFO['client_email']) - assert credentials._token_uri == SERVICE_ACCOUNT_INFO['token_uri'] + credentials = service_account.IDTokenCredentials.from_service_account_info( + SERVICE_ACCOUNT_INFO, target_audience=self.TARGET_AUDIENCE + ) + + assert credentials._signer.key_id == SERVICE_ACCOUNT_INFO["private_key_id"] + assert credentials.service_account_email == SERVICE_ACCOUNT_INFO["client_email"] + assert credentials._token_uri == SERVICE_ACCOUNT_INFO["token_uri"] assert credentials._target_audience == self.TARGET_AUDIENCE def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() - credentials = ( - service_account.IDTokenCredentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE, - target_audience=self.TARGET_AUDIENCE)) + credentials = service_account.IDTokenCredentials.from_service_account_file( + SERVICE_ACCOUNT_JSON_FILE, target_audience=self.TARGET_AUDIENCE + ) - assert credentials.service_account_email == info['client_email'] - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._token_uri == info['token_uri'] + assert credentials.service_account_email == info["client_email"] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._token_uri == info["token_uri"] assert credentials._target_audience == self.TARGET_AUDIENCE def test_default_state(self): @@ -263,7 +266,7 @@ def test_default_state(self): def test_sign_bytes(self): credentials = self.make_credentials() - to_sign = b'123' + to_sign = b"123" signature = credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) @@ -277,26 +280,26 @@ def test_signer_email(self): def test_with_target_audience(self): credentials = self.make_credentials() - new_credentials = credentials.with_target_audience( - 'https://new.example.com') - assert new_credentials._target_audience == 'https://new.example.com' + new_credentials = credentials.with_target_audience("https://new.example.com") + assert new_credentials._target_audience == "https://new.example.com" def test__make_authorization_grant_assertion(self): credentials = self.make_credentials() token = credentials._make_authorization_grant_assertion() payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL - assert payload['aud'] == self.TOKEN_URI - assert payload['target_audience'] == self.TARGET_AUDIENCE + assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL + assert payload["aud"] == self.TOKEN_URI + assert payload["target_audience"] == self.TARGET_AUDIENCE - @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) + @mock.patch("google.oauth2._client.id_token_jwt_grant", autospec=True) def test_refresh_success(self, id_token_jwt_grant): credentials = self.make_credentials() - token = 'token' + token = "token" id_token_jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), - {}) + {}, + ) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials @@ -319,20 +322,22 @@ def test_refresh_success(self, id_token_jwt_grant): # expired) assert credentials.valid - @mock.patch('google.oauth2._client.id_token_jwt_grant', autospec=True) + @mock.patch("google.oauth2._client.id_token_jwt_grant", autospec=True) def test_before_request_refreshes(self, id_token_jwt_grant): credentials = self.make_credentials() - token = 'token' + token = "token" id_token_jwt_grant.return_value = ( - token, _helpers.utcnow() + datetime.timedelta(seconds=500), None) + token, + _helpers.utcnow() + datetime.timedelta(seconds=500), + None, + ) request = mock.create_autospec(transport.Request, instance=True) # Credentials should start as invalid assert not credentials.valid # before_request should cause a refresh - credentials.before_request( - request, 'GET', 'http://example.com?a=1#3', {}) + credentials.before_request(request, "GET", "http://example.com?a=1#3", {}) # The refresh endpoint should've been called. assert id_token_jwt_grant.called diff --git a/tests/test__cloud_sdk.py b/tests/test__cloud_sdk.py index 58c7270b6..c5c5c2769 100644 --- a/tests/test__cloud_sdk.py +++ b/tests/test__cloud_sdk.py @@ -25,29 +25,33 @@ import google.oauth2.credentials -DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') -AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json") with io.open(AUTHORIZED_USER_FILE) as fh: AUTHORIZED_USER_FILE_DATA = json.load(fh) -SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json") with io.open(SERVICE_ACCOUNT_FILE) as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) -with io.open(os.path.join(DATA_DIR, 'cloud_sdk_config.json'), 'rb') as fh: +with io.open(os.path.join(DATA_DIR, "cloud_sdk_config.json"), "rb") as fh: CLOUD_SDK_CONFIG_FILE_DATA = fh.read() -@pytest.mark.parametrize('data, expected_project_id', [ - (CLOUD_SDK_CONFIG_FILE_DATA, 'example-project'), - (b'I am some bad json', None), - (b'{}', None) -]) +@pytest.mark.parametrize( + "data, expected_project_id", + [ + (CLOUD_SDK_CONFIG_FILE_DATA, "example-project"), + (b"I am some bad json", None), + (b"{}", None), + ], +) def test_get_project_id(data, expected_project_id): check_output_patch = mock.patch( - 'subprocess.check_output', autospec=True, return_value=data) + "subprocess.check_output", autospec=True, return_value=data + ) with check_output_patch as check_output: project_id = _cloud_sdk.get_project_id() @@ -57,100 +61,99 @@ def test_get_project_id(data, expected_project_id): @mock.patch( - 'subprocess.check_output', autospec=True, - side_effect=subprocess.CalledProcessError(-1, None)) + "subprocess.check_output", + autospec=True, + side_effect=subprocess.CalledProcessError(-1, None), +) def test_get_project_id_call_error(check_output): project_id = _cloud_sdk.get_project_id() assert project_id is None assert check_output.called -@mock.patch('os.name', new='nt') +@mock.patch("os.name", new="nt") def test_get_project_id_windows(): check_output_patch = mock.patch( - 'subprocess.check_output', autospec=True, - return_value=CLOUD_SDK_CONFIG_FILE_DATA) + "subprocess.check_output", + autospec=True, + return_value=CLOUD_SDK_CONFIG_FILE_DATA, + ) with check_output_patch as check_output: project_id = _cloud_sdk.get_project_id() - assert project_id == 'example-project' + assert project_id == "example-project" assert check_output.called # Make sure the executable is `gcloud.cmd`. args = check_output.call_args[0] command = args[0] executable = command[0] - assert executable == 'gcloud.cmd' + assert executable == "gcloud.cmd" -@mock.patch( - 'google.auth._cloud_sdk.get_config_path', autospec=True) +@mock.patch("google.auth._cloud_sdk.get_config_path", autospec=True) def test_get_application_default_credentials_path(get_config_dir): - config_path = 'config_path' + config_path = "config_path" get_config_dir.return_value = config_path credentials_path = _cloud_sdk.get_application_default_credentials_path() assert credentials_path == os.path.join( - config_path, _cloud_sdk._CREDENTIALS_FILENAME) + config_path, _cloud_sdk._CREDENTIALS_FILENAME + ) def test_get_config_path_env_var(monkeypatch): - config_path_sentinel = 'config_path' - monkeypatch.setenv( - environment_vars.CLOUD_SDK_CONFIG_DIR, config_path_sentinel) + config_path_sentinel = "config_path" + monkeypatch.setenv(environment_vars.CLOUD_SDK_CONFIG_DIR, config_path_sentinel) config_path = _cloud_sdk.get_config_path() assert config_path == config_path_sentinel -@mock.patch('os.path.expanduser') +@mock.patch("os.path.expanduser") def test_get_config_path_unix(expanduser): expanduser.side_effect = lambda path: path config_path = _cloud_sdk.get_config_path() - assert os.path.split(config_path) == ( - '~/.config', _cloud_sdk._CONFIG_DIRECTORY) + assert os.path.split(config_path) == ("~/.config", _cloud_sdk._CONFIG_DIRECTORY) -@mock.patch('os.name', new='nt') +@mock.patch("os.name", new="nt") def test_get_config_path_windows(monkeypatch): - appdata = 'appdata' + appdata = "appdata" monkeypatch.setenv(_cloud_sdk._WINDOWS_CONFIG_ROOT_ENV_VAR, appdata) config_path = _cloud_sdk.get_config_path() - assert os.path.split(config_path) == ( - appdata, _cloud_sdk._CONFIG_DIRECTORY) + assert os.path.split(config_path) == (appdata, _cloud_sdk._CONFIG_DIRECTORY) -@mock.patch('os.name', new='nt') +@mock.patch("os.name", new="nt") def test_get_config_path_no_appdata(monkeypatch): monkeypatch.delenv(_cloud_sdk._WINDOWS_CONFIG_ROOT_ENV_VAR, raising=False) - monkeypatch.setenv('SystemDrive', 'G:') + monkeypatch.setenv("SystemDrive", "G:") config_path = _cloud_sdk.get_config_path() - assert os.path.split(config_path) == ( - 'G:/\\', _cloud_sdk._CONFIG_DIRECTORY) + assert os.path.split(config_path) == ("G:/\\", _cloud_sdk._CONFIG_DIRECTORY) def test_load_authorized_user_credentials(): - credentials = _cloud_sdk.load_authorized_user_credentials( - AUTHORIZED_USER_FILE_DATA) + credentials = _cloud_sdk.load_authorized_user_credentials(AUTHORIZED_USER_FILE_DATA) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert credentials.token is None - assert (credentials._refresh_token == - AUTHORIZED_USER_FILE_DATA['refresh_token']) - assert credentials._client_id == AUTHORIZED_USER_FILE_DATA['client_id'] - assert (credentials._client_secret == - AUTHORIZED_USER_FILE_DATA['client_secret']) - assert (credentials._token_uri == - google.oauth2.credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT) + assert credentials._refresh_token == AUTHORIZED_USER_FILE_DATA["refresh_token"] + assert credentials._client_id == AUTHORIZED_USER_FILE_DATA["client_id"] + assert credentials._client_secret == AUTHORIZED_USER_FILE_DATA["client_secret"] + assert ( + credentials._token_uri + == google.oauth2.credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT + ) def test_load_authorized_user_credentials_bad_format(): with pytest.raises(ValueError) as excinfo: _cloud_sdk.load_authorized_user_credentials({}) - assert excinfo.match(r'missing fields') + assert excinfo.match(r"missing fields") diff --git a/tests/test__default.py b/tests/test__default.py index d1434796f..2c86f3ffb 100644 --- a/tests/test__default.py +++ b/tests/test__default.py @@ -27,94 +27,96 @@ import google.oauth2.credentials -DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') -AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, 'authorized_user.json') +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +AUTHORIZED_USER_FILE = os.path.join(DATA_DIR, "authorized_user.json") with open(AUTHORIZED_USER_FILE) as fh: AUTHORIZED_USER_FILE_DATA = json.load(fh) AUTHORIZED_USER_CLOUD_SDK_FILE = os.path.join( - DATA_DIR, 'authorized_user_cloud_sdk.json') + DATA_DIR, "authorized_user_cloud_sdk.json" +) -SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json") with open(SERVICE_ACCOUNT_FILE) as fh: SERVICE_ACCOUNT_FILE_DATA = json.load(fh) LOAD_FILE_PATCH = mock.patch( - 'google.auth._default._load_credentials_from_file', return_value=( - mock.sentinel.credentials, mock.sentinel.project_id), autospec=True) + "google.auth._default._load_credentials_from_file", + return_value=(mock.sentinel.credentials, mock.sentinel.project_id), + autospec=True, +) def test__load_credentials_from_missing_file(): with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: - _default._load_credentials_from_file('') + _default._load_credentials_from_file("") - assert excinfo.match(r'not found') + assert excinfo.match(r"not found") def test__load_credentials_from_file_invalid_json(tmpdir): - jsonfile = tmpdir.join('invalid.json') - jsonfile.write('{') + jsonfile = tmpdir.join("invalid.json") + jsonfile.write("{") with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(jsonfile)) - assert excinfo.match(r'not a valid json file') + assert excinfo.match(r"not a valid json file") def test__load_credentials_from_file_invalid_type(tmpdir): - jsonfile = tmpdir.join('invalid.json') - jsonfile.write(json.dumps({'type': 'not-a-real-type'})) + jsonfile = tmpdir.join("invalid.json") + jsonfile.write(json.dumps({"type": "not-a-real-type"})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(jsonfile)) - assert excinfo.match(r'does not have a valid type') + assert excinfo.match(r"does not have a valid type") def test__load_credentials_from_file_authorized_user(): - credentials, project_id = _default._load_credentials_from_file( - AUTHORIZED_USER_FILE) + credentials, project_id = _default._load_credentials_from_file(AUTHORIZED_USER_FILE) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert project_id is None def test__load_credentials_from_file_authorized_user_bad_format(tmpdir): - filename = tmpdir.join('authorized_user_bad.json') - filename.write(json.dumps({'type': 'authorized_user'})) + filename = tmpdir.join("authorized_user_bad.json") + filename.write(json.dumps({"type": "authorized_user"})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(filename)) - assert excinfo.match(r'Failed to load authorized user') - assert excinfo.match(r'missing fields') + assert excinfo.match(r"Failed to load authorized user") + assert excinfo.match(r"missing fields") def test__load_credentials_from_file_authorized_user_cloud_sdk(): - with pytest.warns(UserWarning, match='Cloud SDK'): + with pytest.warns(UserWarning, match="Cloud SDK"): credentials, project_id = _default._load_credentials_from_file( - AUTHORIZED_USER_CLOUD_SDK_FILE) + AUTHORIZED_USER_CLOUD_SDK_FILE + ) assert isinstance(credentials, google.oauth2.credentials.Credentials) assert project_id is None def test__load_credentials_from_file_service_account(): - credentials, project_id = _default._load_credentials_from_file( - SERVICE_ACCOUNT_FILE) + credentials, project_id = _default._load_credentials_from_file(SERVICE_ACCOUNT_FILE) assert isinstance(credentials, service_account.Credentials) - assert project_id == SERVICE_ACCOUNT_FILE_DATA['project_id'] + assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"] def test__load_credentials_from_file_service_account_bad_format(tmpdir): - filename = tmpdir.join('serivce_account_bad.json') - filename.write(json.dumps({'type': 'service_account'})) + filename = tmpdir.join("serivce_account_bad.json") + filename.write(json.dumps({"type": "service_account"})) with pytest.raises(exceptions.DefaultCredentialsError) as excinfo: _default._load_credentials_from_file(str(filename)) - assert excinfo.match(r'Failed to load service account') - assert excinfo.match(r'missing fields') + assert excinfo.match(r"Failed to load service account") + assert excinfo.match(r"missing fields") @mock.patch.dict(os.environ, {}, clear=True) @@ -124,19 +126,19 @@ def test__get_explicit_environ_credentials_no_env(): @LOAD_FILE_PATCH def test__get_explicit_environ_credentials(load, monkeypatch): - monkeypatch.setenv(environment_vars.CREDENTIALS, 'filename') + monkeypatch.setenv(environment_vars.CREDENTIALS, "filename") credentials, project_id = _default._get_explicit_environ_credentials() assert credentials is mock.sentinel.credentials assert project_id is mock.sentinel.project_id - load.assert_called_with('filename') + load.assert_called_with("filename") @LOAD_FILE_PATCH def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch): load.return_value = mock.sentinel.credentials, None - monkeypatch.setenv(environment_vars.CREDENTIALS, 'filename') + monkeypatch.setenv(environment_vars.CREDENTIALS, "filename") credentials, project_id = _default._get_explicit_environ_credentials() @@ -146,8 +148,8 @@ def test__get_explicit_environ_credentials_no_project_id(load, monkeypatch): @LOAD_FILE_PATCH @mock.patch( - 'google.auth._cloud_sdk.get_application_default_credentials_path', - autospec=True) + "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True +) def test__get_gcloud_sdk_credentials(get_adc_path, load): get_adc_path.return_value = SERVICE_ACCOUNT_FILE @@ -159,10 +161,10 @@ def test__get_gcloud_sdk_credentials(get_adc_path, load): @mock.patch( - 'google.auth._cloud_sdk.get_application_default_credentials_path', - autospec=True) + "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True +) def test__get_gcloud_sdk_credentials_non_existent(get_adc_path, tmpdir): - non_existent = tmpdir.join('non-existent') + non_existent = tmpdir.join("non-existent") get_adc_path.return_value = str(non_existent) credentials, project_id = _default._get_gcloud_sdk_credentials() @@ -172,12 +174,13 @@ def test__get_gcloud_sdk_credentials_non_existent(get_adc_path, tmpdir): @mock.patch( - 'google.auth._cloud_sdk.get_project_id', - return_value=mock.sentinel.project_id, autospec=True) -@mock.patch('os.path.isfile', return_value=True, autospec=True) + "google.auth._cloud_sdk.get_project_id", + return_value=mock.sentinel.project_id, + autospec=True, +) +@mock.patch("os.path.isfile", return_value=True, autospec=True) @LOAD_FILE_PATCH -def test__get_gcloud_sdk_credentials_project_id( - load, unused_isfile, get_project_id): +def test__get_gcloud_sdk_credentials_project_id(load, unused_isfile, get_project_id): # Don't return a project ID from load file, make the function check # the Cloud SDK project. load.return_value = mock.sentinel.credentials, None @@ -189,13 +192,10 @@ def test__get_gcloud_sdk_credentials_project_id( assert get_project_id.called -@mock.patch( - 'google.auth._cloud_sdk.get_project_id', - return_value=None, autospec=True) -@mock.patch('os.path.isfile', return_value=True) +@mock.patch("google.auth._cloud_sdk.get_project_id", return_value=None, autospec=True) +@mock.patch("os.path.isfile", return_value=True) @LOAD_FILE_PATCH -def test__get_gcloud_sdk_credentials_no_project_id( - load, unused_isfile, get_project_id): +def test__get_gcloud_sdk_credentials_no_project_id(load, unused_isfile, get_project_id): # Don't return a project ID from load file, make the function check # the Cloud SDK project. load.return_value = mock.sentinel.credentials, None @@ -212,6 +212,7 @@ class _AppIdentityModule(object): See https://cloud.google.com/appengine/docs/standard/python/refdocs\ /google.appengine.api.app_identity.app_identity """ + def get_application_id(self): raise NotImplementedError() @@ -219,10 +220,8 @@ def get_application_id(self): @pytest.fixture def app_identity(monkeypatch): """Mocks the app_identity module for google.auth.app_engine.""" - app_identity_module = mock.create_autospec( - _AppIdentityModule, instance=True) - monkeypatch.setattr( - app_engine, 'app_identity', app_identity_module) + app_identity_module = mock.create_autospec(_AppIdentityModule, instance=True) + monkeypatch.setattr(app_engine, "app_identity", app_identity_module) yield app_identity_module @@ -237,8 +236,9 @@ def test__get_gae_credentials(app_identity): def test__get_gae_credentials_no_app_engine(): import sys - with mock.patch.dict('sys.modules'): - sys.modules['google.auth.app_engine'] = None + + with mock.patch.dict("sys.modules"): + sys.modules["google.auth.app_engine"] = None credentials, project_id = _default._get_gae_credentials() assert credentials is None assert project_id is None @@ -249,21 +249,23 @@ def test__get_gae_credentials_no_apis(): @mock.patch( - 'google.auth.compute_engine._metadata.ping', return_value=True, - autospec=True) + "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True +) @mock.patch( - 'google.auth.compute_engine._metadata.get_project_id', - return_value='example-project', autospec=True) + "google.auth.compute_engine._metadata.get_project_id", + return_value="example-project", + autospec=True, +) def test__get_gce_credentials(unused_get, unused_ping): credentials, project_id = _default._get_gce_credentials() assert isinstance(credentials, compute_engine.Credentials) - assert project_id == 'example-project' + assert project_id == "example-project" @mock.patch( - 'google.auth.compute_engine._metadata.ping', return_value=False, - autospec=True) + "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True +) def test__get_gce_credentials_no_ping(unused_ping): credentials, project_id = _default._get_gce_credentials() @@ -272,11 +274,13 @@ def test__get_gce_credentials_no_ping(unused_ping): @mock.patch( - 'google.auth.compute_engine._metadata.ping', return_value=True, - autospec=True) + "google.auth.compute_engine._metadata.ping", return_value=True, autospec=True +) @mock.patch( - 'google.auth.compute_engine._metadata.get_project_id', - side_effect=exceptions.TransportError(), autospec=True) + "google.auth.compute_engine._metadata.get_project_id", + side_effect=exceptions.TransportError(), + autospec=True, +) def test__get_gce_credentials_no_project_id(unused_get, unused_ping): credentials, project_id = _default._get_gce_credentials() @@ -286,110 +290,125 @@ def test__get_gce_credentials_no_project_id(unused_get, unused_ping): def test__get_gce_credentials_no_compute_engine(): import sys - with mock.patch.dict('sys.modules'): - sys.modules['google.auth.compute_engine'] = None + + with mock.patch.dict("sys.modules"): + sys.modules["google.auth.compute_engine"] = None credentials, project_id = _default._get_gce_credentials() assert credentials is None assert project_id is None @mock.patch( - 'google.auth.compute_engine._metadata.ping', return_value=False, - autospec=True) + "google.auth.compute_engine._metadata.ping", return_value=False, autospec=True +) def test__get_gce_credentials_explicit_request(ping): _default._get_gce_credentials(mock.sentinel.request) ping.assert_called_with(request=mock.sentinel.request) @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', + "google.auth._default._get_explicit_environ_credentials", return_value=(mock.sentinel.credentials, mock.sentinel.project_id), - autospec=True) + autospec=True, +) def test_default_early_out(unused_get): - assert _default.default() == ( - mock.sentinel.credentials, mock.sentinel.project_id) + assert _default.default() == (mock.sentinel.credentials, mock.sentinel.project_id) @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', + "google.auth._default._get_explicit_environ_credentials", return_value=(mock.sentinel.credentials, mock.sentinel.project_id), - autospec=True) + autospec=True, +) def test_default_explict_project_id(unused_get, monkeypatch): - monkeypatch.setenv(environment_vars.PROJECT, 'explicit-env') - assert _default.default() == ( - mock.sentinel.credentials, 'explicit-env') + monkeypatch.setenv(environment_vars.PROJECT, "explicit-env") + assert _default.default() == (mock.sentinel.credentials, "explicit-env") @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', + "google.auth._default._get_explicit_environ_credentials", return_value=(mock.sentinel.credentials, mock.sentinel.project_id), - autospec=True) + autospec=True, +) def test_default_explict_legacy_project_id(unused_get, monkeypatch): - monkeypatch.setenv(environment_vars.LEGACY_PROJECT, 'explicit-env') - assert _default.default() == ( - mock.sentinel.credentials, 'explicit-env') + monkeypatch.setenv(environment_vars.LEGACY_PROJECT, "explicit-env") + assert _default.default() == (mock.sentinel.credentials, "explicit-env") +@mock.patch("logging.Logger.warning", autospec=True) @mock.patch( - 'logging.Logger.warning', - autospec=True) -@mock.patch( - 'google.auth._default._get_explicit_environ_credentials', - return_value=(mock.sentinel.credentials, None), autospec=True) + "google.auth._default._get_explicit_environ_credentials", + return_value=(mock.sentinel.credentials, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gcloud_sdk_credentials', - return_value=(mock.sentinel.credentials, None), autospec=True) + "google.auth._default._get_gcloud_sdk_credentials", + return_value=(mock.sentinel.credentials, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gae_credentials', - return_value=(mock.sentinel.credentials, None), autospec=True) + "google.auth._default._get_gae_credentials", + return_value=(mock.sentinel.credentials, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gce_credentials', - return_value=(mock.sentinel.credentials, None), autospec=True) + "google.auth._default._get_gce_credentials", + return_value=(mock.sentinel.credentials, None), + autospec=True, +) def test_default_without_project_id( - unused_gce, unused_gae, unused_sdk, unused_explicit, logger_warning): - assert _default.default() == ( - mock.sentinel.credentials, None) + unused_gce, unused_gae, unused_sdk, unused_explicit, logger_warning +): + assert _default.default() == (mock.sentinel.credentials, None) logger_warning.assert_called_with(mock.ANY, mock.ANY, mock.ANY) @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', - return_value=(None, None), autospec=True) + "google.auth._default._get_explicit_environ_credentials", + return_value=(None, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gcloud_sdk_credentials', - return_value=(None, None), autospec=True) + "google.auth._default._get_gcloud_sdk_credentials", + return_value=(None, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gae_credentials', - return_value=(None, None), autospec=True) + "google.auth._default._get_gae_credentials", + return_value=(None, None), + autospec=True, +) @mock.patch( - 'google.auth._default._get_gce_credentials', - return_value=(None, None), autospec=True) + "google.auth._default._get_gce_credentials", + return_value=(None, None), + autospec=True, +) def test_default_fail(unused_gce, unused_gae, unused_sdk, unused_explicit): with pytest.raises(exceptions.DefaultCredentialsError): assert _default.default() @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', + "google.auth._default._get_explicit_environ_credentials", return_value=(mock.sentinel.credentials, mock.sentinel.project_id), - autospec=True) -@mock.patch( - 'google.auth.credentials.with_scopes_if_required', autospec=True) + autospec=True, +) +@mock.patch("google.auth.credentials.with_scopes_if_required", autospec=True) def test_default_scoped(with_scopes, unused_get): - scopes = ['one', 'two'] + scopes = ["one", "two"] credentials, project_id = _default.default(scopes=scopes) assert credentials == with_scopes.return_value assert project_id == mock.sentinel.project_id - with_scopes.assert_called_once_with( - mock.sentinel.credentials, scopes) + with_scopes.assert_called_once_with(mock.sentinel.credentials, scopes) @mock.patch( - 'google.auth._default._get_explicit_environ_credentials', + "google.auth._default._get_explicit_environ_credentials", return_value=(mock.sentinel.credentials, mock.sentinel.project_id), - autospec=True) + autospec=True, +) def test_default_no_app_engine_compute_engine_module(unused_get): """ google.auth.compute_engine and google.auth.app_engine are both optional @@ -397,8 +416,11 @@ def test_default_no_app_engine_compute_engine_module(unused_get): that default fails gracefully if these modules are absent """ import sys - with mock.patch.dict('sys.modules'): - sys.modules['google.auth.compute_engine'] = None - sys.modules['google.auth.app_engine'] = None + + with mock.patch.dict("sys.modules"): + sys.modules["google.auth.compute_engine"] = None + sys.modules["google.auth.app_engine"] = None assert _default.default() == ( - mock.sentinel.credentials, mock.sentinel.project_id) + mock.sentinel.credentials, + mock.sentinel.project_id, + ) diff --git a/tests/test__helpers.py b/tests/test__helpers.py index 79bdef3d7..3714af7d3 100644 --- a/tests/test__helpers.py +++ b/tests/test__helpers.py @@ -56,20 +56,18 @@ def test_utcnow(): def test_datetime_to_secs(): - assert _helpers.datetime_to_secs( - datetime.datetime(1970, 1, 1)) == 0 - assert _helpers.datetime_to_secs( - datetime.datetime(1990, 5, 29)) == 643939200 + assert _helpers.datetime_to_secs(datetime.datetime(1970, 1, 1)) == 0 + assert _helpers.datetime_to_secs(datetime.datetime(1990, 5, 29)) == 643939200 def test_to_bytes_with_bytes(): - value = b'bytes-val' + value = b"bytes-val" assert _helpers.to_bytes(value) == value def test_to_bytes_with_unicode(): - value = u'string-val' - encoded_value = b'string-val' + value = u"string-val" + encoded_value = b"string-val" assert _helpers.to_bytes(value) == encoded_value @@ -79,13 +77,13 @@ def test_to_bytes_with_nonstring_type(): def test_from_bytes_with_unicode(): - value = u'bytes-val' + value = u"bytes-val" assert _helpers.from_bytes(value) == value def test_from_bytes_with_bytes(): - value = b'string-val' - decoded_value = u'string-val' + value = b"string-val" + decoded_value = u"string-val" assert _helpers.from_bytes(value) == decoded_value @@ -101,53 +99,49 @@ def _assert_query(url, expected): def test_update_query_params_no_params(): - uri = 'http://www.google.com' - updated = _helpers.update_query(uri, {'a': 'b'}) - assert updated == uri + '?a=b' + uri = "http://www.google.com" + updated = _helpers.update_query(uri, {"a": "b"}) + assert updated == uri + "?a=b" def test_update_query_existing_params(): - uri = 'http://www.google.com?x=y' - updated = _helpers.update_query(uri, {'a': 'b', 'c': 'd&'}) - _assert_query(updated, {'x': ['y'], 'a': ['b'], 'c': ['d&']}) + uri = "http://www.google.com?x=y" + updated = _helpers.update_query(uri, {"a": "b", "c": "d&"}) + _assert_query(updated, {"x": ["y"], "a": ["b"], "c": ["d&"]}) def test_update_query_replace_param(): - base_uri = 'http://www.google.com' - uri = base_uri + '?x=a' - updated = _helpers.update_query(uri, {'x': 'b', 'y': 'c'}) - _assert_query(updated, {'x': ['b'], 'y': ['c']}) + base_uri = "http://www.google.com" + uri = base_uri + "?x=a" + updated = _helpers.update_query(uri, {"x": "b", "y": "c"}) + _assert_query(updated, {"x": ["b"], "y": ["c"]}) def test_update_query_remove_param(): - base_uri = 'http://www.google.com' - uri = base_uri + '?x=a' - updated = _helpers.update_query(uri, {'y': 'c'}, remove=['x']) - _assert_query(updated, {'y': ['c']}) + base_uri = "http://www.google.com" + uri = base_uri + "?x=a" + updated = _helpers.update_query(uri, {"y": "c"}, remove=["x"]) + _assert_query(updated, {"y": ["c"]}) def test_scopes_to_string(): cases = [ - ('', ()), - ('', []), - ('', ('',)), - ('', ['', ]), - ('a', ('a',)), - ('b', ['b', ]), - ('a b', ['a', 'b']), - ('a b', ('a', 'b')), - ('a b', (s for s in ['a', 'b'])), + ("", ()), + ("", []), + ("", ("",)), + ("", [""]), + ("a", ("a",)), + ("b", ["b"]), + ("a b", ["a", "b"]), + ("a b", ("a", "b")), + ("a b", (s for s in ["a", "b"])), ] for expected, case in cases: assert _helpers.scopes_to_string(case) == expected def test_string_to_scopes(): - cases = [ - ('', []), - ('a', ['a']), - ('a b c d e f', ['a', 'b', 'c', 'd', 'e', 'f']), - ] + cases = [("", []), ("a", ["a"]), ("a b c d e f", ["a", "b", "c", "d", "e", "f"])] for case, expected in cases: assert _helpers.string_to_scopes(case) == expected @@ -155,14 +149,14 @@ def test_string_to_scopes(): def test_padded_urlsafe_b64decode(): cases = [ - ('YQ==', b'a'), - ('YQ', b'a'), - ('YWE=', b'aa'), - ('YWE', b'aa'), - ('YWFhYQ==', b'aaaa'), - ('YWFhYQ', b'aaaa'), - ('YWFhYWE=', b'aaaaa'), - ('YWFhYWE', b'aaaaa'), + ("YQ==", b"a"), + ("YQ", b"a"), + ("YWE=", b"aa"), + ("YWE", b"aa"), + ("YWFhYQ==", b"aaaa"), + ("YWFhYQ", b"aaaa"), + ("YWFhYWE=", b"aaaaa"), + ("YWFhYWE", b"aaaaa"), ] for case, expected in cases: @@ -170,12 +164,7 @@ def test_padded_urlsafe_b64decode(): def test_unpadded_urlsafe_b64encode(): - cases = [ - (b'', b''), - (b'a', b'YQ'), - (b'aa', b'YWE'), - (b'aaa', b'YWFh'), - ] + cases = [(b"", b""), (b"a", b"YQ"), (b"aa", b"YWE"), (b"aaa", b"YWFh")] for case, expected in cases: assert _helpers.unpadded_urlsafe_b64encode(case) == expected diff --git a/tests/test__oauth2client.py b/tests/test__oauth2client.py index 832b24f10..520f9432a 100644 --- a/tests/test__oauth2client.py +++ b/tests/test__oauth2client.py @@ -26,17 +26,23 @@ from google.auth import _oauth2client -DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") def test__convert_oauth2_credentials(): old_credentials = oauth2client.client.OAuth2Credentials( - 'access_token', 'client_id', 'client_secret', 'refresh_token', - datetime.datetime.min, 'token_uri', 'user_agent', scopes='one two') - - new_credentials = _oauth2client._convert_oauth2_credentials( - old_credentials) + "access_token", + "client_id", + "client_secret", + "refresh_token", + datetime.datetime.min, + "token_uri", + "user_agent", + scopes="one two", + ) + + new_credentials = _oauth2client._convert_oauth2_credentials(old_credentials) assert new_credentials.token == old_credentials.access_token assert new_credentials._refresh_token == old_credentials.refresh_token @@ -48,68 +54,74 @@ def test__convert_oauth2_credentials(): def test__convert_service_account_credentials(): old_class = oauth2client.service_account.ServiceAccountCredentials - old_credentials = old_class.from_json_keyfile_name( - SERVICE_ACCOUNT_JSON_FILE) + old_credentials = old_class.from_json_keyfile_name(SERVICE_ACCOUNT_JSON_FILE) new_credentials = _oauth2client._convert_service_account_credentials( - old_credentials) + old_credentials + ) - assert (new_credentials.service_account_email == - old_credentials.service_account_email) + assert ( + new_credentials.service_account_email == old_credentials.service_account_email + ) assert new_credentials._signer.key_id == old_credentials._private_key_id assert new_credentials._token_uri == old_credentials.token_uri def test__convert_service_account_credentials_with_jwt(): old_class = oauth2client.service_account._JWTAccessCredentials - old_credentials = old_class.from_json_keyfile_name( - SERVICE_ACCOUNT_JSON_FILE) + old_credentials = old_class.from_json_keyfile_name(SERVICE_ACCOUNT_JSON_FILE) new_credentials = _oauth2client._convert_service_account_credentials( - old_credentials) + old_credentials + ) - assert (new_credentials.service_account_email == - old_credentials.service_account_email) + assert ( + new_credentials.service_account_email == old_credentials.service_account_email + ) assert new_credentials._signer.key_id == old_credentials._private_key_id assert new_credentials._token_uri == old_credentials.token_uri def test__convert_gce_app_assertion_credentials(): old_credentials = oauth2client.contrib.gce.AppAssertionCredentials( - email='some_email') + email="some_email" + ) new_credentials = _oauth2client._convert_gce_app_assertion_credentials( - old_credentials) + old_credentials + ) - assert (new_credentials.service_account_email == - old_credentials.service_account_email) + assert ( + new_credentials.service_account_email == old_credentials.service_account_email + ) @pytest.fixture def mock_oauth2client_gae_imports(mock_non_existent_module): - mock_non_existent_module('google.appengine.api.app_identity') - mock_non_existent_module('google.appengine.ext.ndb') - mock_non_existent_module('google.appengine.ext.webapp.util') - mock_non_existent_module('webapp2') + mock_non_existent_module("google.appengine.api.app_identity") + mock_non_existent_module("google.appengine.ext.ndb") + mock_non_existent_module("google.appengine.ext.webapp.util") + mock_non_existent_module("webapp2") -@mock.patch('google.auth.app_engine.app_identity') +@mock.patch("google.auth.app_engine.app_identity") def test__convert_appengine_app_assertion_credentials( - app_identity, mock_oauth2client_gae_imports): + app_identity, mock_oauth2client_gae_imports +): import oauth2client.contrib.appengine - service_account_id = 'service_account_id' + service_account_id = "service_account_id" old_credentials = oauth2client.contrib.appengine.AppAssertionCredentials( - scope='one two', service_account_id=service_account_id) + scope="one two", service_account_id=service_account_id + ) - new_credentials = ( - _oauth2client._convert_appengine_app_assertion_credentials( - old_credentials)) + new_credentials = _oauth2client._convert_appengine_app_assertion_credentials( + old_credentials + ) - assert new_credentials.scopes == ['one', 'two'] - assert (new_credentials._service_account_id == - old_credentials.service_account_id) + assert new_credentials.scopes == ["one", "two"] + assert new_credentials._service_account_id == old_credentials.service_account_id class FakeCredentials(object): @@ -117,10 +129,10 @@ class FakeCredentials(object): def test_convert_success(): - convert_function = mock.Mock(spec=['__call__']) + convert_function = mock.Mock(spec=["__call__"]) conversion_map_patch = mock.patch.object( - _oauth2client, '_CLASS_CONVERSION_MAP', - {FakeCredentials: convert_function}) + _oauth2client, "_CLASS_CONVERSION_MAP", {FakeCredentials: convert_function} + ) credentials = FakeCredentials() with conversion_map_patch: @@ -132,9 +144,9 @@ def test_convert_success(): def test_convert_not_found(): with pytest.raises(ValueError) as excinfo: - _oauth2client.convert('a string is not a real credentials class') + _oauth2client.convert("a string is not a real credentials class") - assert excinfo.match('Unable to convert') + assert excinfo.match("Unable to convert") @pytest.fixture @@ -144,14 +156,15 @@ def reset__oauth2client_module(): def test_import_has_app_engine( - mock_oauth2client_gae_imports, reset__oauth2client_module): + mock_oauth2client_gae_imports, reset__oauth2client_module +): reload_module(_oauth2client) assert _oauth2client._HAS_APPENGINE def test_import_without_oauth2client(monkeypatch, reset__oauth2client_module): - monkeypatch.setitem(sys.modules, 'oauth2client', None) + monkeypatch.setitem(sys.modules, "oauth2client", None) with pytest.raises(ImportError) as excinfo: reload_module(_oauth2client) - assert excinfo.match('oauth2client') + assert excinfo.match("oauth2client") diff --git a/tests/test__service_account_info.py b/tests/test__service_account_info.py index ef41e2757..4419f670b 100644 --- a/tests/test__service_account_info.py +++ b/tests/test__service_account_info.py @@ -22,42 +22,41 @@ from google.auth import crypt -DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) def test_from_dict(): signer = _service_account_info.from_dict(SERVICE_ACCOUNT_INFO) assert isinstance(signer, crypt.RSASigner) - assert signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id'] + assert signer.key_id == SERVICE_ACCOUNT_INFO["private_key_id"] def test_from_dict_bad_private_key(): info = SERVICE_ACCOUNT_INFO.copy() - info['private_key'] = 'garbage' + info["private_key"] = "garbage" with pytest.raises(ValueError) as excinfo: _service_account_info.from_dict(info) - assert excinfo.match(r'key') + assert excinfo.match(r"key") def test_from_dict_bad_format(): with pytest.raises(ValueError) as excinfo: - _service_account_info.from_dict({}, require=('meep',)) + _service_account_info.from_dict({}, require=("meep",)) - assert excinfo.match(r'missing fields') + assert excinfo.match(r"missing fields") def test_from_filename(): - info, signer = _service_account_info.from_filename( - SERVICE_ACCOUNT_JSON_FILE) + info, signer = _service_account_info.from_filename(SERVICE_ACCOUNT_JSON_FILE) for key, value in six.iteritems(SERVICE_ACCOUNT_INFO): assert info[key] == value assert isinstance(signer, crypt.RSASigner) - assert signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id'] + assert signer.key_id == SERVICE_ACCOUNT_INFO["private_key_id"] diff --git a/tests/test_app_engine.py b/tests/test_app_engine.py index 70fa5ca33..9559a2cd2 100644 --- a/tests/test_app_engine.py +++ b/tests/test_app_engine.py @@ -25,6 +25,7 @@ class _AppIdentityModule(object): See https://cloud.google.com/appengine/docs/standard/python/refdocs /google.appengine.api.app_identity.app_identity """ + def get_application_id(self): raise NotImplementedError() @@ -41,10 +42,8 @@ def get_access_token(self, scopes, service_account_id=None): @pytest.fixture def app_identity(monkeypatch): """Mocks the app_identity module for google.auth.app_engine.""" - app_identity_module = mock.create_autospec( - _AppIdentityModule, instance=True) - monkeypatch.setattr( - app_engine, 'app_identity', app_identity_module) + app_identity_module = mock.create_autospec(_AppIdentityModule, instance=True) + monkeypatch.setattr(app_engine, "app_identity", app_identity_module) yield app_identity_module @@ -57,13 +56,15 @@ def test_get_project_id_missing_apis(): with pytest.raises(EnvironmentError) as excinfo: assert app_engine.get_project_id() - assert excinfo.match(r'App Engine APIs are not available') + assert excinfo.match(r"App Engine APIs are not available") class TestSigner(object): def test_key_id(self, app_identity): app_identity.sign_blob.return_value = ( - mock.sentinel.key_id, mock.sentinel.signature) + mock.sentinel.key_id, + mock.sentinel.signature, + ) signer = app_engine.Signer() @@ -71,10 +72,12 @@ def test_key_id(self, app_identity): def test_sign(self, app_identity): app_identity.sign_blob.return_value = ( - mock.sentinel.key_id, mock.sentinel.signature) + mock.sentinel.key_id, + mock.sentinel.signature, + ) signer = app_engine.Signer() - to_sign = b'123' + to_sign = b"123" signature = signer.sign(to_sign) @@ -87,7 +90,7 @@ def test_missing_apis(self): with pytest.raises(EnvironmentError) as excinfo: app_engine.Credentials() - assert excinfo.match(r'App Engine APIs are not available') + assert excinfo.match(r"App Engine APIs are not available") def test_default_state(self, app_identity): credentials = app_engine.Credentials() @@ -106,52 +109,52 @@ def test_with_scopes(self, app_identity): assert not credentials.scopes assert credentials.requires_scopes - scoped_credentials = credentials.with_scopes(['email']) + scoped_credentials = credentials.with_scopes(["email"]) - assert scoped_credentials.has_scopes(['email']) + assert scoped_credentials.has_scopes(["email"]) assert not scoped_credentials.requires_scopes def test_service_account_email_implicit(self, app_identity): app_identity.get_service_account_name.return_value = ( - mock.sentinel.service_account_email) + mock.sentinel.service_account_email + ) credentials = app_engine.Credentials() - assert (credentials.service_account_email == - mock.sentinel.service_account_email) + assert credentials.service_account_email == mock.sentinel.service_account_email assert app_identity.get_service_account_name.called def test_service_account_email_explicit(self, app_identity): credentials = app_engine.Credentials( - service_account_id=mock.sentinel.service_account_email) + service_account_id=mock.sentinel.service_account_email + ) - assert (credentials.service_account_email == - mock.sentinel.service_account_email) + assert credentials.service_account_email == mock.sentinel.service_account_email assert not app_identity.get_service_account_name.called - @mock.patch( - 'google.auth._helpers.utcnow', - return_value=datetime.datetime.min) + @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min) def test_refresh(self, utcnow, app_identity): - token = 'token' + token = "token" ttl = 643942923 app_identity.get_access_token.return_value = token, ttl - credentials = app_engine.Credentials(scopes=['email']) + credentials = app_engine.Credentials(scopes=["email"]) credentials.refresh(None) app_identity.get_access_token.assert_called_with( - credentials.scopes, credentials._service_account_id) + credentials.scopes, credentials._service_account_id + ) assert credentials.token == token - assert credentials.expiry == datetime.datetime( - 1990, 5, 29, 1, 2, 3) + assert credentials.expiry == datetime.datetime(1990, 5, 29, 1, 2, 3) assert credentials.valid assert not credentials.expired def test_sign_bytes(self, app_identity): app_identity.sign_blob.return_value = ( - mock.sentinel.key_id, mock.sentinel.signature) + mock.sentinel.key_id, + mock.sentinel.signature, + ) credentials = app_engine.Credentials() - to_sign = b'123' + to_sign = b"123" signature = credentials.sign_bytes(to_sign) diff --git a/tests/test_credentials.py b/tests/test_credentials.py index b302989fb..2a89b01b6 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -35,7 +35,7 @@ def test_credentials_constructor(): def test_expired_and_valid(): credentials = CredentialsImpl() - credentials.token = 'token' + credentials.token = "token" assert credentials.valid assert not credentials.expired @@ -43,9 +43,8 @@ def test_expired_and_valid(): # Set the expiration to one second more than now plus the clock skew # accomodation. These credentials should be valid. credentials.expiry = ( - datetime.datetime.utcnow() + - _helpers.CLOCK_SKEW + - datetime.timedelta(seconds=1)) + datetime.datetime.utcnow() + _helpers.CLOCK_SKEW + datetime.timedelta(seconds=1) + ) assert credentials.valid assert not credentials.expired @@ -60,23 +59,23 @@ def test_expired_and_valid(): def test_before_request(): credentials = CredentialsImpl() - request = 'token' + request = "token" headers = {} # First call should call refresh, setting the token. - credentials.before_request(request, 'http://example.com', 'GET', headers) + credentials.before_request(request, "http://example.com", "GET", headers) assert credentials.valid - assert credentials.token == 'token' - assert headers['authorization'] == 'Bearer token' + assert credentials.token == "token" + assert headers["authorization"] == "Bearer token" - request = 'token2' + request = "token2" headers = {} # Second call shouldn't call refresh. - credentials.before_request(request, 'http://example.com', 'GET', headers) + credentials.before_request(request, "http://example.com", "GET", headers) assert credentials.valid - assert credentials.token == 'token' - assert headers['authorization'] == 'Bearer token' + assert credentials.token == "token" + assert headers["authorization"] == "Bearer token" def test_anonymous_credentials_ctor(): @@ -100,21 +99,20 @@ def test_anonymous_credentials_apply_default(): anon.apply(headers) assert headers == {} with pytest.raises(ValueError): - anon.apply(headers, token='TOKEN') + anon.apply(headers, token="TOKEN") def test_anonymous_credentials_before_request(): anon = credentials.AnonymousCredentials() request = object() - method = 'GET' - url = 'https://example.com/api/endpoint' + method = "GET" + url = "https://example.com/api/endpoint" headers = {} anon.before_request(request, method, url, headers) assert headers == {} -class ReadOnlyScopedCredentialsImpl( - credentials.ReadOnlyScoped, CredentialsImpl): +class ReadOnlyScopedCredentialsImpl(credentials.ReadOnlyScoped, CredentialsImpl): @property def requires_scopes(self): return super(ReadOnlyScopedCredentialsImpl, self).requires_scopes @@ -127,12 +125,12 @@ def test_readonly_scoped_credentials_constructor(): def test_readonly_scoped_credentials_scopes(): credentials = ReadOnlyScopedCredentialsImpl() - credentials._scopes = ['one', 'two'] - assert credentials.scopes == ['one', 'two'] - assert credentials.has_scopes(['one']) - assert credentials.has_scopes(['two']) - assert credentials.has_scopes(['one', 'two']) - assert not credentials.has_scopes(['three']) + credentials._scopes = ["one", "two"] + assert credentials.scopes == ["one", "two"] + assert credentials.has_scopes(["one"]) + assert credentials.has_scopes(["two"]) + assert credentials.has_scopes(["one", "two"]) + assert not credentials.has_scopes(["three"]) def test_readonly_scoped_credentials_requires_scopes(): @@ -156,16 +154,18 @@ def with_scopes(self, scopes): def test_create_scoped_if_required_scoped(): unscoped_credentials = RequiresScopedCredentialsImpl() scoped_credentials = credentials.with_scopes_if_required( - unscoped_credentials, ['one', 'two']) + unscoped_credentials, ["one", "two"] + ) assert scoped_credentials is not unscoped_credentials assert not scoped_credentials.requires_scopes - assert scoped_credentials.has_scopes(['one', 'two']) + assert scoped_credentials.has_scopes(["one", "two"]) def test_create_scoped_if_required_not_scopes(): unscoped_credentials = CredentialsImpl() scoped_credentials = credentials.with_scopes_if_required( - unscoped_credentials, ['one', 'two']) + unscoped_credentials, ["one", "two"] + ) assert scoped_credentials is unscoped_credentials diff --git a/tests/test_iam.py b/tests/test_iam.py index cc090850f..52ab9bd82 100644 --- a/tests/test_iam.py +++ b/tests/test_iam.py @@ -32,7 +32,7 @@ def make_request(status, data=None): response.status = status if data is not None: - response.data = json.dumps(data).encode('utf-8') + response.data = json.dumps(data).encode("utf-8") request = mock.create_autospec(transport.Request) request.return_value = response @@ -43,7 +43,7 @@ def make_credentials(): class CredentialsImpl(google.auth.credentials.Credentials): def __init__(self): super(CredentialsImpl, self).__init__() - self.token = 'token' + self.token = "token" # Force refresh self.expiry = datetime.datetime.min + _helpers.CLOCK_SKEW @@ -57,35 +57,33 @@ class TestSigner(object): def test_constructor(self): request = mock.sentinel.request credentials = mock.create_autospec( - google.auth.credentials.Credentials, instance=True) + google.auth.credentials.Credentials, instance=True + ) - signer = iam.Signer( - request, credentials, mock.sentinel.service_account_email) + signer = iam.Signer(request, credentials, mock.sentinel.service_account_email) assert signer._request == mock.sentinel.request assert signer._credentials == credentials - assert (signer._service_account_email == - mock.sentinel.service_account_email) + assert signer._service_account_email == mock.sentinel.service_account_email def test_key_id(self): signer = iam.Signer( mock.sentinel.request, mock.sentinel.credentials, - mock.sentinel.service_account_email) + mock.sentinel.service_account_email, + ) assert signer.key_id is None def test_sign_bytes(self): - signature = b'DEADBEEF' - encoded_signature = base64.b64encode(signature).decode('utf-8') - request = make_request( - http_client.OK, data={'signature': encoded_signature}) + signature = b"DEADBEEF" + encoded_signature = base64.b64encode(signature).decode("utf-8") + request = make_request(http_client.OK, data={"signature": encoded_signature}) credentials = make_credentials() - signer = iam.Signer( - request, credentials, mock.sentinel.service_account_email) + signer = iam.Signer(request, credentials, mock.sentinel.service_account_email) - returned_signature = signer.sign('123') + returned_signature = signer.sign("123") assert returned_signature == signature @@ -93,8 +91,7 @@ def test_sign_bytes_failure(self): request = make_request(http_client.UNAUTHORIZED) credentials = make_credentials() - signer = iam.Signer( - request, credentials, mock.sentinel.service_account_email) + signer = iam.Signer(request, credentials, mock.sentinel.service_account_email) with pytest.raises(exceptions.TransportError): - signer.sign('123') + signer.sign("123") diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py index 9945401d4..1cfcc7c6c 100644 --- a/tests/test_impersonated_credentials.py +++ b/tests/test_impersonated_credentials.py @@ -28,35 +28,38 @@ from google.auth.impersonated_credentials import Credentials from google.oauth2 import service_account -DATA_DIR = os.path.join(os.path.dirname(__file__), '', 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "", "data") -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -ID_TOKEN_DATA = ('eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyOTNhZDk3N2Ew' - 'Yjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwc' - 'zovL2Zvby5iYXIiLCJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJle' - 'HAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwiaXNzIjoiaHR0cHM6L' - 'y9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwN' - 'zA4NTY4In0.redacted') +ID_TOKEN_DATA = ( + "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyOTNhZDk3N2Ew" + "Yjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwc" + "zovL2Zvby5iYXIiLCJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJle" + "HAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwiaXNzIjoiaHR0cHM6L" + "y9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwN" + "zA4NTY4In0.redacted" +) ID_TOKEN_EXPIRY = 1564475051 -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) -SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') -TOKEN_URI = 'https://example.com/oauth2/token' +SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1") +TOKEN_URI = "https://example.com/oauth2/token" @pytest.fixture def mock_donor_credentials(): - with mock.patch('google.oauth2._client.jwt_grant', autospec=True) as grant: + with mock.patch("google.oauth2._client.jwt_grant", autospec=True) as grant: grant.return_value = ( "source token", _helpers.utcnow() + datetime.timedelta(seconds=500), - {}) + {}, + ) yield grant @@ -71,54 +74,51 @@ def json(self): @pytest.fixture def mock_authorizedsession_sign(): - with mock.patch('google.auth.transport.requests.AuthorizedSession.request', - autospec=True) as auth_session: - data = { - "keyId": "1", - "signedBlob": "c2lnbmF0dXJl" - } + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.request", autospec=True + ) as auth_session: + data = {"keyId": "1", "signedBlob": "c2lnbmF0dXJl"} auth_session.return_value = MockResponse(data, http_client.OK) yield auth_session @pytest.fixture def mock_authorizedsession_idtoken(): - with mock.patch('google.auth.transport.requests.AuthorizedSession.request', - autospec=True) as auth_session: - data = { - "token": ID_TOKEN_DATA - } + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.request", autospec=True + ) as auth_session: + data = {"token": ID_TOKEN_DATA} auth_session.return_value = MockResponse(data, http_client.OK) yield auth_session class TestImpersonatedCredentials(object): - SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' - TARGET_PRINCIPAL = 'impersonated@project.iam.gserviceaccount.com' - TARGET_SCOPES = ['https://www.googleapis.com/auth/devstorage.read_only'] + SERVICE_ACCOUNT_EMAIL = "service-account@example.com" + TARGET_PRINCIPAL = "impersonated@project.iam.gserviceaccount.com" + TARGET_SCOPES = ["https://www.googleapis.com/auth/devstorage.read_only"] DELEGATES = [] LIFETIME = 3600 SOURCE_CREDENTIALS = service_account.Credentials( - SIGNER, SERVICE_ACCOUNT_EMAIL, TOKEN_URI) + SIGNER, SERVICE_ACCOUNT_EMAIL, TOKEN_URI + ) - def make_credentials(self, lifetime=LIFETIME, - target_principal=TARGET_PRINCIPAL): + def make_credentials(self, lifetime=LIFETIME, target_principal=TARGET_PRINCIPAL): return Credentials( source_credentials=self.SOURCE_CREDENTIALS, target_principal=target_principal, target_scopes=self.TARGET_SCOPES, delegates=self.DELEGATES, - lifetime=lifetime) + lifetime=lifetime, + ) def test_default_state(self): credentials = self.make_credentials() assert not credentials.valid assert credentials.expired - def make_request(self, data, status=http_client.OK, - headers=None, side_effect=None): + def make_request(self, data, status=http_client.OK, headers=None, side_effect=None): response = mock.create_autospec(transport.Response, instance=False) response.status = status response.data = _helpers.to_bytes(data) @@ -132,40 +132,34 @@ def make_request(self, data, status=http_client.OK, def test_refresh_success(self, mock_donor_credentials): credentials = self.make_credentials(lifetime=None) - token = 'token' + token = "token" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) credentials.refresh(request) assert credentials.valid assert not credentials.expired - def test_refresh_failure_malformed_expire_time( - self, mock_donor_credentials): + def test_refresh_failure_malformed_expire_time(self, mock_donor_credentials): credentials = self.make_credentials(lifetime=None) - token = 'token' + token = "token" - expire_time = ( - _helpers.utcnow() + datetime.timedelta(seconds=500)).isoformat('T') - response_body = { - "accessToken": token, - "expireTime": expire_time - } + expire_time = (_helpers.utcnow() + datetime.timedelta(seconds=500)).isoformat( + "T" + ) + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) with pytest.raises(exceptions.RefreshError) as excinfo: credentials.refresh(request) @@ -180,15 +174,15 @@ def test_refresh_failure_unauthorzed(self, mock_donor_credentials): response_body = { "error": { - "code": 403, - "message": "The caller does not have permission", - "status": "PERMISSION_DENIED" + "code": 403, + "message": "The caller does not have permission", + "status": "PERMISSION_DENIED", } } request = self.make_request( - data=json.dumps(response_body), - status=http_client.UNAUTHORIZED) + data=json.dumps(response_body), status=http_client.UNAUTHORIZED + ) with pytest.raises(exceptions.RefreshError) as excinfo: credentials.refresh(request) @@ -204,8 +198,8 @@ def test_refresh_failure_http_error(self, mock_donor_credentials): response_body = {} request = self.make_request( - data=json.dumps(response_body), - status=http_client.HTTPException) + data=json.dumps(response_body), status=http_client.HTTPException + ) with pytest.raises(exceptions.RefreshError) as excinfo: credentials.refresh(request) @@ -221,31 +215,24 @@ def test_expired(self): def test_signer(self): credentials = self.make_credentials() - assert isinstance(credentials.signer, - impersonated_credentials.Credentials) + assert isinstance(credentials.signer, impersonated_credentials.Credentials) def test_signer_email(self): - credentials = self.make_credentials( - target_principal=self.TARGET_PRINCIPAL) + credentials = self.make_credentials(target_principal=self.TARGET_PRINCIPAL) assert credentials.signer_email == self.TARGET_PRINCIPAL def test_service_account_email(self): - credentials = self.make_credentials( - target_principal=self.TARGET_PRINCIPAL) + credentials = self.make_credentials(target_principal=self.TARGET_PRINCIPAL) assert credentials.service_account_email == self.TARGET_PRINCIPAL - def test_sign_bytes(self, mock_donor_credentials, - mock_authorizedsession_sign): + def test_sign_bytes(self, mock_donor_credentials, mock_authorizedsession_sign): credentials = self.make_credentials(lifetime=None) - token = 'token' + token = "token" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - token_response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + token_response_body = {"accessToken": token, "expireTime": expire_time} response = mock.create_autospec(transport.Response, instance=False) response.status = http_client.OK @@ -259,26 +246,24 @@ def test_sign_bytes(self, mock_donor_credentials, assert credentials.valid assert not credentials.expired - signature = credentials.sign_bytes(b'signed bytes') - assert signature == b'signature' + signature = credentials.sign_bytes(b"signed bytes") + assert signature == b"signature" - def test_id_token_success(self, mock_donor_credentials, - mock_authorizedsession_idtoken): + def test_id_token_success( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): credentials = self.make_credentials(lifetime=None) - token = 'token' - target_audience = 'https://foo.bar' + token = "token" + target_audience = "https://foo.bar" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) credentials.refresh(request) @@ -286,30 +271,28 @@ def test_id_token_success(self, mock_donor_credentials, assert not credentials.expired id_creds = impersonated_credentials.IDTokenCredentials( - credentials, target_audience=target_audience) + credentials, target_audience=target_audience + ) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA - assert id_creds.expiry == datetime.datetime.fromtimestamp( - ID_TOKEN_EXPIRY) + assert id_creds.expiry == datetime.datetime.fromtimestamp(ID_TOKEN_EXPIRY) - def test_id_token_from_credential(self, mock_donor_credentials, - mock_authorizedsession_idtoken): + def test_id_token_from_credential( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): credentials = self.make_credentials(lifetime=None) - token = 'token' - target_audience = 'https://foo.bar' + token = "token" + target_audience = "https://foo.bar" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) credentials.refresh(request) @@ -317,72 +300,66 @@ def test_id_token_from_credential(self, mock_donor_credentials, assert not credentials.expired id_creds = impersonated_credentials.IDTokenCredentials( - credentials, target_audience=target_audience) + credentials, target_audience=target_audience + ) id_creds = id_creds.from_credentials(target_credentials=credentials) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA - def test_id_token_with_target_audience(self, mock_donor_credentials, - mock_authorizedsession_idtoken): + def test_id_token_with_target_audience( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): credentials = self.make_credentials(lifetime=None) - token = 'token' - target_audience = 'https://foo.bar' + token = "token" + target_audience = "https://foo.bar" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) credentials.refresh(request) assert credentials.valid assert not credentials.expired - id_creds = impersonated_credentials.IDTokenCredentials( - credentials) - id_creds = id_creds.with_target_audience( - target_audience=target_audience) + id_creds = impersonated_credentials.IDTokenCredentials(credentials) + id_creds = id_creds.with_target_audience(target_audience=target_audience) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA - assert id_creds.expiry == datetime.datetime.fromtimestamp( - ID_TOKEN_EXPIRY) + assert id_creds.expiry == datetime.datetime.fromtimestamp(ID_TOKEN_EXPIRY) - def test_id_token_invalid_cred(self, mock_donor_credentials, - mock_authorizedsession_idtoken): + def test_id_token_invalid_cred( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): credentials = None with pytest.raises(exceptions.GoogleAuthError) as excinfo: impersonated_credentials.IDTokenCredentials(credentials) - assert excinfo.match('Provided Credential must be' - ' impersonated_credentials') + assert excinfo.match("Provided Credential must be" " impersonated_credentials") - def test_id_token_with_include_email(self, mock_donor_credentials, - mock_authorizedsession_idtoken): + def test_id_token_with_include_email( + self, mock_donor_credentials, mock_authorizedsession_idtoken + ): credentials = self.make_credentials(lifetime=None) - token = 'token' - target_audience = 'https://foo.bar' + token = "token" + target_audience = "https://foo.bar" expire_time = ( - _helpers.utcnow().replace(microsecond=0) + - datetime.timedelta(seconds=500)).isoformat('T') + 'Z' - response_body = { - "accessToken": token, - "expireTime": expire_time - } + _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) + ).isoformat("T") + "Z" + response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( - data=json.dumps(response_body), - status=http_client.OK) + data=json.dumps(response_body), status=http_client.OK + ) credentials.refresh(request) @@ -390,7 +367,8 @@ def test_id_token_with_include_email(self, mock_donor_credentials, assert not credentials.expired id_creds = impersonated_credentials.IDTokenCredentials( - credentials, target_audience=target_audience) + credentials, target_audience=target_audience + ) id_creds = id_creds.with_include_email(True) id_creds.refresh(request) diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 4a64717ff..b0c6e48e9 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -26,41 +26,45 @@ from google.auth import jwt -DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") -with open(os.path.join(DATA_DIR, 'privatekey.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh: PRIVATE_KEY_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'public_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh: PUBLIC_CERT_BYTES = fh.read() -with open(os.path.join(DATA_DIR, 'other_cert.pem'), 'rb') as fh: +with open(os.path.join(DATA_DIR, "other_cert.pem"), "rb") as fh: OTHER_CERT_BYTES = fh.read() -SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, 'service_account.json') +SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json") -with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: +with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: SERVICE_ACCOUNT_INFO = json.load(fh) @pytest.fixture def signer(): - return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, '1') + return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1") def test_encode_basic(signer): - test_payload = {'test': 'value'} + test_payload = {"test": "value"} encoded = jwt.encode(signer, test_payload) header, payload, _, _ = jwt._unverified_decode(encoded) assert payload == test_payload - assert header == {'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id} + assert header == {"typ": "JWT", "alg": "RS256", "kid": signer.key_id} def test_encode_extra_headers(signer): - encoded = jwt.encode(signer, {}, header={'extra': 'value'}) + encoded = jwt.encode(signer, {}, header={"extra": "value"}) header = jwt.decode_header(encoded) assert header == { - 'typ': 'JWT', 'alg': 'RS256', 'kid': signer.key_id, 'extra': 'value'} + "typ": "JWT", + "alg": "RS256", + "kid": signer.key_id, + "extra": "value", + } @pytest.fixture @@ -68,11 +72,11 @@ def token_factory(signer): def factory(claims=None, key_id=None): now = _helpers.datetime_to_secs(_helpers.utcnow()) payload = { - 'aud': 'audience@example.com', - 'iat': now, - 'exp': now + 300, - 'user': 'billy bob', - 'metadata': {'meta': 'data'} + "aud": "audience@example.com", + "iat": now, + "exp": now + 300, + "user": "billy bob", + "metadata": {"meta": "data"}, } payload.update(claims or {}) @@ -83,154 +87,168 @@ def factory(claims=None, key_id=None): key_id = None return jwt.encode(signer, payload, key_id=key_id) + return factory def test_decode_valid(token_factory): payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES) - assert payload['aud'] == 'audience@example.com' - assert payload['user'] == 'billy bob' - assert payload['metadata']['meta'] == 'data' + assert payload["aud"] == "audience@example.com" + assert payload["user"] == "billy bob" + assert payload["metadata"]["meta"] == "data" def test_decode_valid_with_audience(token_factory): payload = jwt.decode( - token_factory(), certs=PUBLIC_CERT_BYTES, - audience='audience@example.com') - assert payload['aud'] == 'audience@example.com' - assert payload['user'] == 'billy bob' - assert payload['metadata']['meta'] == 'data' + token_factory(), certs=PUBLIC_CERT_BYTES, audience="audience@example.com" + ) + assert payload["aud"] == "audience@example.com" + assert payload["user"] == "billy bob" + assert payload["metadata"]["meta"] == "data" def test_decode_valid_unverified(token_factory): payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False) - assert payload['aud'] == 'audience@example.com' - assert payload['user'] == 'billy bob' - assert payload['metadata']['meta'] == 'data' + assert payload["aud"] == "audience@example.com" + assert payload["user"] == "billy bob" + assert payload["metadata"]["meta"] == "data" def test_decode_bad_token_wrong_number_of_segments(): with pytest.raises(ValueError) as excinfo: - jwt.decode('1.2', PUBLIC_CERT_BYTES) - assert excinfo.match(r'Wrong number of segments') + jwt.decode("1.2", PUBLIC_CERT_BYTES) + assert excinfo.match(r"Wrong number of segments") def test_decode_bad_token_not_base64(): with pytest.raises((ValueError, TypeError)) as excinfo: - jwt.decode('1.2.3', PUBLIC_CERT_BYTES) - assert excinfo.match(r'Incorrect padding|more than a multiple of 4') + jwt.decode("1.2.3", PUBLIC_CERT_BYTES) + assert excinfo.match(r"Incorrect padding|more than a multiple of 4") def test_decode_bad_token_not_json(): - token = b'.'.join([base64.urlsafe_b64encode(b'123!')] * 3) + token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) - assert excinfo.match(r'Can\'t parse segment') + assert excinfo.match(r"Can\'t parse segment") def test_decode_bad_token_no_iat_or_exp(signer): - token = jwt.encode(signer, {'test': 'value'}) + token = jwt.encode(signer, {"test": "value"}) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) - assert excinfo.match(r'Token does not contain required claim') + assert excinfo.match(r"Token does not contain required claim") def test_decode_bad_token_too_early(token_factory): - token = token_factory(claims={ - 'iat': _helpers.datetime_to_secs( - _helpers.utcnow() + datetime.timedelta(hours=1)) - }) + token = token_factory( + claims={ + "iat": _helpers.datetime_to_secs( + _helpers.utcnow() + datetime.timedelta(hours=1) + ) + } + ) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) - assert excinfo.match(r'Token used too early') + assert excinfo.match(r"Token used too early") def test_decode_bad_token_expired(token_factory): - token = token_factory(claims={ - 'exp': _helpers.datetime_to_secs( - _helpers.utcnow() - datetime.timedelta(hours=1)) - }) + token = token_factory( + claims={ + "exp": _helpers.datetime_to_secs( + _helpers.utcnow() - datetime.timedelta(hours=1) + ) + } + ) with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES) - assert excinfo.match(r'Token expired') + assert excinfo.match(r"Token expired") def test_decode_bad_token_wrong_audience(token_factory): token = token_factory() - audience = 'audience2@example.com' + audience = "audience2@example.com" with pytest.raises(ValueError) as excinfo: jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience) - assert excinfo.match(r'Token has wrong audience') + assert excinfo.match(r"Token has wrong audience") def test_decode_wrong_cert(token_factory): with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), OTHER_CERT_BYTES) - assert excinfo.match(r'Could not verify token signature') + assert excinfo.match(r"Could not verify token signature") def test_decode_multicert_bad_cert(token_factory): - certs = {'1': OTHER_CERT_BYTES, '2': PUBLIC_CERT_BYTES} + certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES} with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), certs) - assert excinfo.match(r'Could not verify token signature') + assert excinfo.match(r"Could not verify token signature") def test_decode_no_cert(token_factory): - certs = {'2': PUBLIC_CERT_BYTES} + certs = {"2": PUBLIC_CERT_BYTES} with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), certs) - assert excinfo.match(r'Certificate for key id 1 not found') + assert excinfo.match(r"Certificate for key id 1 not found") def test_decode_no_key_id(token_factory): token = token_factory(key_id=False) - certs = {'2': PUBLIC_CERT_BYTES} + certs = {"2": PUBLIC_CERT_BYTES} payload = jwt.decode(token, certs) - assert payload['user'] == 'billy bob' + assert payload["user"] == "billy bob" def test_roundtrip_explicit_key_id(token_factory): - token = token_factory(key_id='3') - certs = {'2': OTHER_CERT_BYTES, '3': PUBLIC_CERT_BYTES} + token = token_factory(key_id="3") + certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES} payload = jwt.decode(token, certs) - assert payload['user'] == 'billy bob' + assert payload["user"] == "billy bob" class TestCredentials(object): - SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' - SUBJECT = 'subject' - AUDIENCE = 'audience' - ADDITIONAL_CLAIMS = {'meta': 'data'} + SERVICE_ACCOUNT_EMAIL = "service-account@example.com" + SUBJECT = "subject" + AUDIENCE = "audience" + ADDITIONAL_CLAIMS = {"meta": "data"} credentials = None @pytest.fixture(autouse=True) def credentials_fixture(self, signer): self.credentials = jwt.Credentials( - signer, self.SERVICE_ACCOUNT_EMAIL, self.SERVICE_ACCOUNT_EMAIL, - self.AUDIENCE) + signer, + self.SERVICE_ACCOUNT_EMAIL, + self.SERVICE_ACCOUNT_EMAIL, + self.AUDIENCE, + ) def test_from_service_account_info(self): - with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: + with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: info = json.load(fh) credentials = jwt.Credentials.from_service_account_info( - info, audience=self.AUDIENCE) + info, audience=self.AUDIENCE + ) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] - assert credentials._subject == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] + assert credentials._subject == info["client_email"] assert credentials._audience == self.AUDIENCE def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_info( - info, subject=self.SUBJECT, audience=self.AUDIENCE, - additional_claims=self.ADDITIONAL_CLAIMS) - - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] + info, + subject=self.SUBJECT, + audience=self.AUDIENCE, + additional_claims=self.ADDITIONAL_CLAIMS, + ) + + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] assert credentials._subject == self.SUBJECT assert credentials._audience == self.AUDIENCE assert credentials._additional_claims == self.ADDITIONAL_CLAIMS @@ -239,33 +257,37 @@ def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE) + SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE + ) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] - assert credentials._subject == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] + assert credentials._subject == info["client_email"] assert credentials._audience == self.AUDIENCE def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.Credentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE, subject=self.SUBJECT, - audience=self.AUDIENCE, additional_claims=self.ADDITIONAL_CLAIMS) - - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] + SERVICE_ACCOUNT_JSON_FILE, + subject=self.SUBJECT, + audience=self.AUDIENCE, + additional_claims=self.ADDITIONAL_CLAIMS, + ) + + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] assert credentials._subject == self.SUBJECT assert credentials._audience == self.AUDIENCE assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_signing_credentials(self): jwt_from_signing = self.credentials.from_signing_credentials( - self.credentials, - audience=mock.sentinel.new_audience) + self.credentials, audience=mock.sentinel.new_audience + ) jwt_from_info = jwt.Credentials.from_service_account_info( - SERVICE_ACCOUNT_INFO, - audience=mock.sentinel.new_audience) + SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience + ) assert isinstance(jwt_from_signing, jwt.Credentials) assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id @@ -279,19 +301,17 @@ def test_default_state(self): assert not self.credentials.expired def test_with_claims(self): - new_audience = 'new_audience' - new_credentials = self.credentials.with_claims( - audience=new_audience) + new_audience = "new_audience" + new_credentials = self.credentials.with_claims(audience=new_audience) assert new_credentials._signer == self.credentials._signer assert new_credentials._issuer == self.credentials._issuer assert new_credentials._subject == self.credentials._subject assert new_credentials._audience == new_audience - assert (new_credentials._additional_claims == - self.credentials._additional_claims) + assert new_credentials._additional_claims == self.credentials._additional_claims def test_sign_bytes(self): - to_sign = b'123' + to_sign = b"123" signature = self.credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) @@ -299,12 +319,11 @@ def test_signer(self): assert isinstance(self.credentials.signer, crypt.RSASigner) def test_signer_email(self): - assert (self.credentials.signer_email == - SERVICE_ACCOUNT_INFO['client_email']) + assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"] def _verify_token(self, token): payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL + assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL return payload def test_refresh(self): @@ -318,7 +337,7 @@ def test_expired(self): self.credentials.refresh(None) assert not self.credentials.expired - with mock.patch('google.auth._helpers.utcnow') as now: + with mock.patch("google.auth._helpers.utcnow") as now: one_day = datetime.timedelta(days=1) now.return_value = self.credentials.expiry + one_day assert self.credentials.expired @@ -328,55 +347,58 @@ def test_before_request(self): self.credentials.refresh(None) self.credentials.before_request( - None, 'GET', 'http://example.com?a=1#3', headers) + None, "GET", "http://example.com?a=1#3", headers + ) - header_value = headers['authorization'] - _, token = header_value.split(' ') + header_value = headers["authorization"] + _, token = header_value.split(" ") # Since the audience is set, it should use the existing token. - assert token.encode('utf-8') == self.credentials.token + assert token.encode("utf-8") == self.credentials.token payload = self._verify_token(token) - assert payload['aud'] == self.AUDIENCE + assert payload["aud"] == self.AUDIENCE def test_before_request_refreshes(self): assert not self.credentials.valid - self.credentials.before_request( - None, 'GET', 'http://example.com?a=1#3', {}) + self.credentials.before_request(None, "GET", "http://example.com?a=1#3", {}) assert self.credentials.valid class TestOnDemandCredentials(object): - SERVICE_ACCOUNT_EMAIL = 'service-account@example.com' - SUBJECT = 'subject' - ADDITIONAL_CLAIMS = {'meta': 'data'} + SERVICE_ACCOUNT_EMAIL = "service-account@example.com" + SUBJECT = "subject" + ADDITIONAL_CLAIMS = {"meta": "data"} credentials = None @pytest.fixture(autouse=True) def credentials_fixture(self, signer): self.credentials = jwt.OnDemandCredentials( - signer, self.SERVICE_ACCOUNT_EMAIL, self.SERVICE_ACCOUNT_EMAIL, - max_cache_size=2) + signer, + self.SERVICE_ACCOUNT_EMAIL, + self.SERVICE_ACCOUNT_EMAIL, + max_cache_size=2, + ) def test_from_service_account_info(self): - with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh: + with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh: info = json.load(fh) credentials = jwt.OnDemandCredentials.from_service_account_info(info) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] - assert credentials._subject == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] + assert credentials._subject == info["client_email"] def test_from_service_account_info_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_info( - info, subject=self.SUBJECT, - additional_claims=self.ADDITIONAL_CLAIMS) + info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS + ) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] assert credentials._subject == self.SUBJECT assert credentials._additional_claims == self.ADDITIONAL_CLAIMS @@ -384,29 +406,32 @@ def test_from_service_account_file(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE) + SERVICE_ACCOUNT_JSON_FILE + ) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] - assert credentials._subject == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] + assert credentials._subject == info["client_email"] def test_from_service_account_file_args(self): info = SERVICE_ACCOUNT_INFO.copy() credentials = jwt.OnDemandCredentials.from_service_account_file( - SERVICE_ACCOUNT_JSON_FILE, subject=self.SUBJECT, - additional_claims=self.ADDITIONAL_CLAIMS) + SERVICE_ACCOUNT_JSON_FILE, + subject=self.SUBJECT, + additional_claims=self.ADDITIONAL_CLAIMS, + ) - assert credentials._signer.key_id == info['private_key_id'] - assert credentials._issuer == info['client_email'] + assert credentials._signer.key_id == info["private_key_id"] + assert credentials._issuer == info["client_email"] assert credentials._subject == self.SUBJECT assert credentials._additional_claims == self.ADDITIONAL_CLAIMS def test_from_signing_credentials(self): - jwt_from_signing = self.credentials.from_signing_credentials( - self.credentials) + jwt_from_signing = self.credentials.from_signing_credentials(self.credentials) jwt_from_info = jwt.OnDemandCredentials.from_service_account_info( - SERVICE_ACCOUNT_INFO) + SERVICE_ACCOUNT_INFO + ) assert isinstance(jwt_from_signing, jwt.OnDemandCredentials) assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id @@ -420,9 +445,8 @@ def test_default_state(self): assert not self.credentials.expired def test_with_claims(self): - new_claims = {'meep': 'moop'} - new_credentials = self.credentials.with_claims( - additional_claims=new_claims) + new_claims = {"meep": "moop"} + new_credentials = self.credentials.with_claims(additional_claims=new_claims) assert new_credentials._signer == self.credentials._signer assert new_credentials._issuer == self.credentials._issuer @@ -430,7 +454,7 @@ def test_with_claims(self): assert new_credentials._additional_claims == new_claims def test_sign_bytes(self): - to_sign = b'123' + to_sign = b"123" signature = self.credentials.sign_bytes(to_sign) assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES) @@ -438,12 +462,11 @@ def test_signer(self): assert isinstance(self.credentials.signer, crypt.RSASigner) def test_signer_email(self): - assert (self.credentials.signer_email == - SERVICE_ACCOUNT_INFO['client_email']) + assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"] def _verify_token(self, token): payload = jwt.decode(token, PUBLIC_CERT_BYTES) - assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL + assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL return payload def test_refresh(self): @@ -454,25 +477,27 @@ def test_before_request(self): headers = {} self.credentials.before_request( - None, 'GET', 'http://example.com?a=1#3', headers) + None, "GET", "http://example.com?a=1#3", headers + ) - _, token = headers['authorization'].split(' ') + _, token = headers["authorization"].split(" ") payload = self._verify_token(token) - assert payload['aud'] == 'http://example.com' + assert payload["aud"] == "http://example.com" # Making another request should re-use the same token. - self.credentials.before_request( - None, 'GET', 'http://example.com?b=2', headers) + self.credentials.before_request(None, "GET", "http://example.com?b=2", headers) - _, new_token = headers['authorization'].split(' ') + _, new_token = headers["authorization"].split(" ") assert new_token == token def test_expired_token(self): - self.credentials._cache['audience'] = ( - mock.sentinel.token, datetime.datetime.min) + self.credentials._cache["audience"] = ( + mock.sentinel.token, + datetime.datetime.min, + ) - token = self.credentials._get_jwt_for_audience('audience') + token = self.credentials._get_jwt_for_audience("audience") assert token != mock.sentinel.token diff --git a/tests/transport/compliance.py b/tests/transport/compliance.py index 50c6d7ccf..dc7c58be5 100644 --- a/tests/transport/compliance.py +++ b/tests/transport/compliance.py @@ -22,12 +22,11 @@ from google.auth import exceptions # .invalid will never resolve, see https://tools.ietf.org/html/rfc2606 -NXDOMAIN = 'test.invalid' +NXDOMAIN = "test.invalid" class RequestResponseTests(object): - - @pytest.fixture(scope='module') + @pytest.fixture(scope="module") def server(self): """Provides a test HTTP server. @@ -40,20 +39,21 @@ def server(self): # pylint: disable=unused-variable # (pylint thinks the flask routes are unusued.) - @app.route('/basic') + @app.route("/basic") def index(): - header_value = flask.request.headers.get('x-test-header', 'value') - headers = {'X-Test-Header': header_value} - return 'Basic Content', http_client.OK, headers + header_value = flask.request.headers.get("x-test-header", "value") + headers = {"X-Test-Header": header_value} + return "Basic Content", http_client.OK, headers - @app.route('/server_error') + @app.route("/server_error") def server_error(): - return 'Error', http_client.INTERNAL_SERVER_ERROR + return "Error", http_client.INTERNAL_SERVER_ERROR - @app.route('/wait') + @app.route("/wait") def wait(): time.sleep(3) - return 'Waited' + return "Waited" + # pylint: enable=unused-variable server = WSGIServer(application=app.wsgi_app) @@ -63,44 +63,46 @@ def wait(): def test_request_basic(self, server): request = self.make_request() - response = request(url=server.url + '/basic', method='GET') + response = request(url=server.url + "/basic", method="GET") assert response.status == http_client.OK - assert response.headers['x-test-header'] == 'value' - assert response.data == b'Basic Content' + assert response.headers["x-test-header"] == "value" + assert response.data == b"Basic Content" def test_request_with_timeout_success(self, server): request = self.make_request() - response = request(url=server.url + '/basic', method='GET', timeout=2) + response = request(url=server.url + "/basic", method="GET", timeout=2) assert response.status == http_client.OK - assert response.headers['x-test-header'] == 'value' - assert response.data == b'Basic Content' + assert response.headers["x-test-header"] == "value" + assert response.data == b"Basic Content" def test_request_with_timeout_failure(self, server): request = self.make_request() with pytest.raises(exceptions.TransportError): - request(url=server.url + '/wait', method='GET', timeout=1) + request(url=server.url + "/wait", method="GET", timeout=1) def test_request_headers(self, server): request = self.make_request() response = request( - url=server.url + '/basic', method='GET', headers={ - 'x-test-header': 'hello world'}) + url=server.url + "/basic", + method="GET", + headers={"x-test-header": "hello world"}, + ) assert response.status == http_client.OK - assert response.headers['x-test-header'] == 'hello world' - assert response.data == b'Basic Content' + assert response.headers["x-test-header"] == "hello world" + assert response.data == b"Basic Content" def test_request_error(self, server): request = self.make_request() - response = request(url=server.url + '/server_error', method='GET') + response = request(url=server.url + "/server_error", method="GET") assert response.status == http_client.INTERNAL_SERVER_ERROR - assert response.data == b'Error' + assert response.data == b"Error" def test_connection_error(self): request = self.make_request() with pytest.raises(exceptions.TransportError): - request(url='http://{}'.format(NXDOMAIN), method='GET') + request(url="http://{}".format(NXDOMAIN), method="GET") diff --git a/tests/transport/test__http_client.py b/tests/transport/test__http_client.py index 6b69088b8..9e7f108ae 100644 --- a/tests/transport/test__http_client.py +++ b/tests/transport/test__http_client.py @@ -26,6 +26,6 @@ def make_request(self): def test_non_http(self): request = self.make_request() with pytest.raises(exceptions.TransportError) as excinfo: - request(url='https://{}'.format(compliance.NXDOMAIN), method='GET') + request(url="https://{}".format(compliance.NXDOMAIN), method="GET") - assert excinfo.match('https') + assert excinfo.match("https") diff --git a/tests/transport/test_grpc.py b/tests/transport/test_grpc.py index c98dcff67..810d038aa 100644 --- a/tests/transport/test_grpc.py +++ b/tests/transport/test_grpc.py @@ -25,22 +25,23 @@ # pylint: disable=ungrouped-imports import grpc import google.auth.transport.grpc + HAS_GRPC = True except ImportError: # pragma: NO COVER HAS_GRPC = False -pytestmark = pytest.mark.skipif(not HAS_GRPC, reason='gRPC is unavailable.') +pytestmark = pytest.mark.skipif(not HAS_GRPC, reason="gRPC is unavailable.") class CredentialsStub(credentials.Credentials): - def __init__(self, token='token'): + def __init__(self, token="token"): super(CredentialsStub, self).__init__() self.token = token self.expiry = None def refresh(self, request): - self.token += '1' + self.token += "1" class TestAuthMetadataPlugin(object): @@ -48,8 +49,7 @@ def test_call_no_refresh(self): credentials = CredentialsStub() request = mock.create_autospec(transport.Request) - plugin = google.auth.transport.grpc.AuthMetadataPlugin( - credentials, request) + plugin = google.auth.transport.grpc.AuthMetadataPlugin(credentials, request) context = mock.create_autospec(grpc.AuthMetadataContext, instance=True) context.method_name = mock.sentinel.method_name @@ -59,15 +59,15 @@ def test_call_no_refresh(self): plugin(context, callback) callback.assert_called_once_with( - [(u'authorization', u'Bearer {}'.format(credentials.token))], None) + [(u"authorization", u"Bearer {}".format(credentials.token))], None + ) def test_call_refresh(self): credentials = CredentialsStub() credentials.expiry = datetime.datetime.min + _helpers.CLOCK_SKEW request = mock.create_autospec(transport.Request) - plugin = google.auth.transport.grpc.AuthMetadataPlugin( - credentials, request) + plugin = google.auth.transport.grpc.AuthMetadataPlugin(credentials, request) context = mock.create_autospec(grpc.AuthMetadataContext, instance=True) context.method_name = mock.sentinel.method_name @@ -76,29 +76,33 @@ def test_call_refresh(self): plugin(context, callback) - assert credentials.token == 'token1' + assert credentials.token == "token1" callback.assert_called_once_with( - [(u'authorization', u'Bearer {}'.format(credentials.token))], None) + [(u"authorization", u"Bearer {}".format(credentials.token))], None + ) -@mock.patch('grpc.composite_channel_credentials', autospec=True) -@mock.patch('grpc.metadata_call_credentials', autospec=True) -@mock.patch('grpc.ssl_channel_credentials', autospec=True) -@mock.patch('grpc.secure_channel', autospec=True) +@mock.patch("grpc.composite_channel_credentials", autospec=True) +@mock.patch("grpc.metadata_call_credentials", autospec=True) +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("grpc.secure_channel", autospec=True) def test_secure_authorized_channel( - secure_channel, ssl_channel_credentials, metadata_call_credentials, - composite_channel_credentials): + secure_channel, + ssl_channel_credentials, + metadata_call_credentials, + composite_channel_credentials, +): credentials = CredentialsStub() request = mock.create_autospec(transport.Request) - target = 'example.com:80' + target = "example.com:80" channel = google.auth.transport.grpc.secure_authorized_channel( - credentials, request, target, options=mock.sentinel.options) + credentials, request, target, options=mock.sentinel.options + ) # Check the auth plugin construction. auth_plugin = metadata_call_credentials.call_args[0][0] - assert isinstance( - auth_plugin, google.auth.transport.grpc.AuthMetadataPlugin) + assert isinstance(auth_plugin, google.auth.transport.grpc.AuthMetadataPlugin) assert auth_plugin._credentials == credentials assert auth_plugin._request == request @@ -107,35 +111,41 @@ def test_secure_authorized_channel( # Check the composite credentials call. composite_channel_credentials.assert_called_once_with( - ssl_channel_credentials.return_value, - metadata_call_credentials.return_value) + ssl_channel_credentials.return_value, metadata_call_credentials.return_value + ) # Check the channel call. secure_channel.assert_called_once_with( - target, composite_channel_credentials.return_value, - options=mock.sentinel.options) + target, + composite_channel_credentials.return_value, + options=mock.sentinel.options, + ) assert channel == secure_channel.return_value -@mock.patch('grpc.composite_channel_credentials', autospec=True) -@mock.patch('grpc.metadata_call_credentials', autospec=True) -@mock.patch('grpc.ssl_channel_credentials', autospec=True) -@mock.patch('grpc.secure_channel', autospec=True) +@mock.patch("grpc.composite_channel_credentials", autospec=True) +@mock.patch("grpc.metadata_call_credentials", autospec=True) +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("grpc.secure_channel", autospec=True) def test_secure_authorized_channel_explicit_ssl( - secure_channel, ssl_channel_credentials, metadata_call_credentials, - composite_channel_credentials): + secure_channel, + ssl_channel_credentials, + metadata_call_credentials, + composite_channel_credentials, +): credentials = mock.Mock() request = mock.Mock() - target = 'example.com:80' + target = "example.com:80" ssl_credentials = mock.Mock() google.auth.transport.grpc.secure_authorized_channel( - credentials, request, target, ssl_credentials=ssl_credentials) + credentials, request, target, ssl_credentials=ssl_credentials + ) # Check the ssl channel call. assert not ssl_channel_credentials.called # Check the composite credentials call. composite_channel_credentials.assert_called_once_with( - ssl_credentials, - metadata_call_credentials.return_value) + ssl_credentials, metadata_call_credentials.return_value + ) diff --git a/tests/transport/test_requests.py b/tests/transport/test_requests.py index 311992ae9..0e165ac54 100644 --- a/tests/transport/test_requests.py +++ b/tests/transport/test_requests.py @@ -29,24 +29,24 @@ def make_request(self): def test_timeout(self): http = mock.create_autospec(requests.Session, instance=True) request = google.auth.transport.requests.Request(http) - request(url='http://example.com', method='GET', timeout=5) + request(url="http://example.com", method="GET", timeout=5) - assert http.request.call_args[1]['timeout'] == 5 + assert http.request.call_args[1]["timeout"] == 5 class CredentialsStub(google.auth.credentials.Credentials): - def __init__(self, token='token'): + def __init__(self, token="token"): super(CredentialsStub, self).__init__() self.token = token def apply(self, headers, token=None): - headers['authorization'] = self.token + headers["authorization"] = self.token def before_request(self, request, method, url, headers): self.apply(headers) def refresh(self, request): - self.token += '1' + self.token += "1" class AdapterStub(requests.adapters.BaseAdapter): @@ -77,11 +77,12 @@ def make_response(status=http_client.OK, data=None): class TestAuthorizedHttp(object): - TEST_URL = 'http://example.com/' + TEST_URL = "http://example.com/" def test_constructor(self): authed_session = google.auth.transport.requests.AuthorizedSession( - mock.sentinel.credentials) + mock.sentinel.credentials + ) assert authed_session.credentials == mock.sentinel.credentials @@ -90,7 +91,8 @@ def test_constructor_with_auth_request(self): auth_request = google.auth.transport.requests.Request(http) authed_session = google.auth.transport.requests.AuthorizedSession( - mock.sentinel.credentials, auth_request=auth_request) + mock.sentinel.credentials, auth_request=auth_request + ) assert authed_session._auth_request == auth_request @@ -99,32 +101,30 @@ def test_request_no_refresh(self): response = make_response() adapter = AdapterStub([response]) - authed_session = google.auth.transport.requests.AuthorizedSession( - credentials) + authed_session = google.auth.transport.requests.AuthorizedSession(credentials) authed_session.mount(self.TEST_URL, adapter) - result = authed_session.request('GET', self.TEST_URL) + result = authed_session.request("GET", self.TEST_URL) assert response == result assert credentials.before_request.called assert not credentials.refresh.called assert len(adapter.requests) == 1 assert adapter.requests[0].url == self.TEST_URL - assert adapter.requests[0].headers['authorization'] == 'token' + assert adapter.requests[0].headers["authorization"] == "token" def test_request_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) final_response = make_response(status=http_client.OK) # First request will 401, second request will succeed. - adapter = AdapterStub([ - make_response(status=http_client.UNAUTHORIZED), - final_response]) + adapter = AdapterStub( + [make_response(status=http_client.UNAUTHORIZED), final_response] + ) - authed_session = google.auth.transport.requests.AuthorizedSession( - credentials) + authed_session = google.auth.transport.requests.AuthorizedSession(credentials) authed_session.mount(self.TEST_URL, adapter) - result = authed_session.request('GET', self.TEST_URL) + result = authed_session.request("GET", self.TEST_URL) assert result == final_response assert credentials.before_request.call_count == 2 @@ -132,7 +132,7 @@ def test_request_refresh(self): assert len(adapter.requests) == 2 assert adapter.requests[0].url == self.TEST_URL - assert adapter.requests[0].headers['authorization'] == 'token' + assert adapter.requests[0].headers["authorization"] == "token" assert adapter.requests[1].url == self.TEST_URL - assert adapter.requests[1].headers['authorization'] == 'token1' + assert adapter.requests[1].headers["authorization"] == "token1" diff --git a/tests/transport/test_urllib3.py b/tests/transport/test_urllib3.py index a119b74c9..1d7ce5a0e 100644 --- a/tests/transport/test_urllib3.py +++ b/tests/transport/test_urllib3.py @@ -29,35 +29,35 @@ def make_request(self): def test_timeout(self): http = mock.create_autospec(urllib3.PoolManager) request = google.auth.transport.urllib3.Request(http) - request(url='http://example.com', method='GET', timeout=5) + request(url="http://example.com", method="GET", timeout=5) - assert http.request.call_args[1]['timeout'] == 5 + assert http.request.call_args[1]["timeout"] == 5 def test__make_default_http_with_certifi(): http = google.auth.transport.urllib3._make_default_http() - assert 'cert_reqs' in http.connection_pool_kw + assert "cert_reqs" in http.connection_pool_kw -@mock.patch.object(google.auth.transport.urllib3, 'certifi', new=None) +@mock.patch.object(google.auth.transport.urllib3, "certifi", new=None) def test__make_default_http_without_certifi(): http = google.auth.transport.urllib3._make_default_http() - assert 'cert_reqs' not in http.connection_pool_kw + assert "cert_reqs" not in http.connection_pool_kw class CredentialsStub(google.auth.credentials.Credentials): - def __init__(self, token='token'): + def __init__(self, token="token"): super(CredentialsStub, self).__init__() self.token = token def apply(self, headers, token=None): - headers['authorization'] = self.token + headers["authorization"] = self.token def before_request(self, request, method, url, headers): self.apply(headers) def refresh(self, request): - self.token += '1' + self.token += "1" class HttpStub(object): @@ -78,11 +78,12 @@ def __init__(self, status=http_client.OK, data=None): class TestAuthorizedHttp(object): - TEST_URL = 'http://example.com' + TEST_URL = "http://example.com" def test_authed_http_defaults(self): authed_http = google.auth.transport.urllib3.AuthorizedHttp( - mock.sentinel.credentials) + mock.sentinel.credentials + ) assert authed_http.credentials == mock.sentinel.credentials assert isinstance(authed_http.http, urllib3.PoolManager) @@ -93,40 +94,41 @@ def test_urlopen_no_refresh(self): http = HttpStub([response]) authed_http = google.auth.transport.urllib3.AuthorizedHttp( - credentials, http=http) + credentials, http=http + ) - result = authed_http.urlopen('GET', self.TEST_URL) + result = authed_http.urlopen("GET", self.TEST_URL) assert result == response assert credentials.before_request.called assert not credentials.refresh.called assert http.requests == [ - ('GET', self.TEST_URL, None, {'authorization': 'token'}, {})] + ("GET", self.TEST_URL, None, {"authorization": "token"}, {}) + ] def test_urlopen_refresh(self): credentials = mock.Mock(wraps=CredentialsStub()) final_response = ResponseStub(status=http_client.OK) # First request will 401, second request will succeed. - http = HttpStub([ - ResponseStub(status=http_client.UNAUTHORIZED), - final_response]) + http = HttpStub([ResponseStub(status=http_client.UNAUTHORIZED), final_response]) authed_http = google.auth.transport.urllib3.AuthorizedHttp( - credentials, http=http) + credentials, http=http + ) - authed_http = authed_http.urlopen('GET', 'http://example.com') + authed_http = authed_http.urlopen("GET", "http://example.com") assert authed_http == final_response assert credentials.before_request.call_count == 2 assert credentials.refresh.called assert http.requests == [ - ('GET', self.TEST_URL, None, {'authorization': 'token'}, {}), - ('GET', self.TEST_URL, None, {'authorization': 'token1'}, {})] + ("GET", self.TEST_URL, None, {"authorization": "token"}, {}), + ("GET", self.TEST_URL, None, {"authorization": "token1"}, {}), + ] def test_proxies(self): http = mock.create_autospec(urllib3.PoolManager) - authed_http = google.auth.transport.urllib3.AuthorizedHttp( - None, http=http) + authed_http = google.auth.transport.urllib3.AuthorizedHttp(None, http=http) with authed_http: pass From 58074ac5f4c1e17a8b1b09b5da1d19d944aada44 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 21 Oct 2019 16:52:11 -0700 Subject: [PATCH 2/2] chore: add flake8 file --- .flake8 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..0574e0a3a --- /dev/null +++ b/.flake8 @@ -0,0 +1,8 @@ +[flake8] +ignore = E203, E266, E501, W503 +exclude = + # Standard linting exemptions. + __pycache__, + .git, + *.pyc, + conf.py