Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +1 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/exceptions.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/lazy_wheel.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/package_streaming.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/url.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/einops/__pycache__/einops.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/einops/__pycache__/parsing.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/__pycache__/pluggable.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_cloud_sdk.py +159 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_credentials_async.py +176 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_helpers.py +245 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_service_account_info.py +81 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/app_engine.py +179 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/jwt.py +857 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/version.py +15 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_client.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_client_async.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_credentials_async.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_id_token_async.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_reauth_async.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_service_account_async.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/challenges.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/credentials.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/gdch_credentials.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/id_token.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/reauth.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/service_account.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/sts.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/utils.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/_id_token_async.py +287 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/_reauth_async.py +329 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/credentials.py +490 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/gdch_credentials.py +251 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/id_token.py +341 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/message.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/service_reflection.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/type_pb2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/compiler/__init__.py +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/compiler/plugin_pb2.py +35 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/_api_implementation.cpython-38-x86_64-linux-gnu.so +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/builder.py +130 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/containers.py +710 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/type_checkers.py +435 -0
.gitattributes
CHANGED
|
@@ -381,3 +381,4 @@ my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/
|
|
| 381 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 382 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/widgets.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
|
| 383 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 381 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 382 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/widgets.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
|
| 383 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf filter=lfs diff=lfs merge=lfs -text
|
| 384 |
+
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Oblique.ttf filter=lfs diff=lfs merge=lfs -text
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (175 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/exceptions.cpython-38.pyc
ADDED
|
Binary file (1.27 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/lazy_wheel.cpython-38.pyc
ADDED
|
Binary file (8.84 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/package_streaming.cpython-38.pyc
ADDED
|
Binary file (5.15 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/conda_package_streaming/__pycache__/url.cpython-38.pyc
ADDED
|
Binary file (2.45 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/einops/__pycache__/einops.cpython-38.pyc
ADDED
|
Binary file (20.9 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/einops/__pycache__/parsing.cpython-38.pyc
ADDED
|
Binary file (4.92 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/__pycache__/pluggable.cpython-38.pyc
ADDED
|
Binary file (9.19 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_cloud_sdk.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 Google Inc.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Helpers for reading the Google Cloud SDK's configuration."""
|
| 16 |
+
|
| 17 |
+
import json
|
| 18 |
+
import os
|
| 19 |
+
import subprocess
|
| 20 |
+
|
| 21 |
+
import six
|
| 22 |
+
|
| 23 |
+
from google.auth import environment_vars
|
| 24 |
+
from google.auth import exceptions
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# The ~/.config subdirectory containing gcloud credentials.
|
| 28 |
+
_CONFIG_DIRECTORY = "gcloud"
|
| 29 |
+
# Windows systems store config at %APPDATA%\gcloud
|
| 30 |
+
_WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA"
|
| 31 |
+
# The name of the file in the Cloud SDK config that contains default
|
| 32 |
+
# credentials.
|
| 33 |
+
_CREDENTIALS_FILENAME = "application_default_credentials.json"
|
| 34 |
+
# The name of the Cloud SDK shell script
|
| 35 |
+
_CLOUD_SDK_POSIX_COMMAND = "gcloud"
|
| 36 |
+
_CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd"
|
| 37 |
+
# The command to get the Cloud SDK configuration
|
| 38 |
+
_CLOUD_SDK_CONFIG_COMMAND = ("config", "config-helper", "--format", "json")
|
| 39 |
+
# The command to get google user access token
|
| 40 |
+
_CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token")
|
| 41 |
+
# Cloud SDK's application-default client ID
|
| 42 |
+
CLOUD_SDK_CLIENT_ID = (
|
| 43 |
+
"764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def get_config_path():
|
| 48 |
+
"""Returns the absolute path the the Cloud SDK's configuration directory.
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
str: The Cloud SDK config path.
|
| 52 |
+
"""
|
| 53 |
+
# If the path is explicitly set, return that.
|
| 54 |
+
try:
|
| 55 |
+
return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR]
|
| 56 |
+
except KeyError:
|
| 57 |
+
pass
|
| 58 |
+
|
| 59 |
+
# Non-windows systems store this at ~/.config/gcloud
|
| 60 |
+
if os.name != "nt":
|
| 61 |
+
return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY)
|
| 62 |
+
# Windows systems store config at %APPDATA%\gcloud
|
| 63 |
+
else:
|
| 64 |
+
try:
|
| 65 |
+
return os.path.join(
|
| 66 |
+
os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY
|
| 67 |
+
)
|
| 68 |
+
except KeyError:
|
| 69 |
+
# This should never happen unless someone is really
|
| 70 |
+
# messing with things, but we'll cover the case anyway.
|
| 71 |
+
drive = os.environ.get("SystemDrive", "C:")
|
| 72 |
+
return os.path.join(drive, "\\", _CONFIG_DIRECTORY)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def get_application_default_credentials_path():
|
| 76 |
+
"""Gets the path to the application default credentials file.
|
| 77 |
+
|
| 78 |
+
The path may or may not exist.
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
str: The full path to application default credentials.
|
| 82 |
+
"""
|
| 83 |
+
config_path = get_config_path()
|
| 84 |
+
return os.path.join(config_path, _CREDENTIALS_FILENAME)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _run_subprocess_ignore_stderr(command):
|
| 88 |
+
""" Return subprocess.check_output with the given command and ignores stderr."""
|
| 89 |
+
with open(os.devnull, "w") as devnull:
|
| 90 |
+
output = subprocess.check_output(command, stderr=devnull)
|
| 91 |
+
return output
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def get_project_id():
|
| 95 |
+
"""Gets the project ID from the Cloud SDK.
|
| 96 |
+
|
| 97 |
+
Returns:
|
| 98 |
+
Optional[str]: The project ID.
|
| 99 |
+
"""
|
| 100 |
+
if os.name == "nt":
|
| 101 |
+
command = _CLOUD_SDK_WINDOWS_COMMAND
|
| 102 |
+
else:
|
| 103 |
+
command = _CLOUD_SDK_POSIX_COMMAND
|
| 104 |
+
|
| 105 |
+
try:
|
| 106 |
+
# Ignore the stderr coming from gcloud, so it won't be mixed into the output.
|
| 107 |
+
# https://github.com/googleapis/google-auth-library-python/issues/673
|
| 108 |
+
output = _run_subprocess_ignore_stderr((command,) + _CLOUD_SDK_CONFIG_COMMAND)
|
| 109 |
+
except (subprocess.CalledProcessError, OSError, IOError):
|
| 110 |
+
return None
|
| 111 |
+
|
| 112 |
+
try:
|
| 113 |
+
configuration = json.loads(output.decode("utf-8"))
|
| 114 |
+
except ValueError:
|
| 115 |
+
return None
|
| 116 |
+
|
| 117 |
+
try:
|
| 118 |
+
return configuration["configuration"]["properties"]["core"]["project"]
|
| 119 |
+
except KeyError:
|
| 120 |
+
return None
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def get_auth_access_token(account=None):
|
| 124 |
+
"""Load user access token with the ``gcloud auth print-access-token`` command.
|
| 125 |
+
|
| 126 |
+
Args:
|
| 127 |
+
account (Optional[str]): Account to get the access token for. If not
|
| 128 |
+
specified, the current active account will be used.
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
str: The user access token.
|
| 132 |
+
|
| 133 |
+
Raises:
|
| 134 |
+
google.auth.exceptions.UserAccessTokenError: if failed to get access
|
| 135 |
+
token from gcloud.
|
| 136 |
+
"""
|
| 137 |
+
if os.name == "nt":
|
| 138 |
+
command = _CLOUD_SDK_WINDOWS_COMMAND
|
| 139 |
+
else:
|
| 140 |
+
command = _CLOUD_SDK_POSIX_COMMAND
|
| 141 |
+
|
| 142 |
+
try:
|
| 143 |
+
if account:
|
| 144 |
+
command = (
|
| 145 |
+
(command,)
|
| 146 |
+
+ _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
|
| 147 |
+
+ ("--account=" + account,)
|
| 148 |
+
)
|
| 149 |
+
else:
|
| 150 |
+
command = (command,) + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
|
| 151 |
+
|
| 152 |
+
access_token = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
| 153 |
+
# remove the trailing "\n"
|
| 154 |
+
return access_token.decode("utf-8").strip()
|
| 155 |
+
except (subprocess.CalledProcessError, OSError, IOError) as caught_exc:
|
| 156 |
+
new_exc = exceptions.UserAccessTokenError(
|
| 157 |
+
"Failed to obtain access token", caught_exc
|
| 158 |
+
)
|
| 159 |
+
six.raise_from(new_exc, caught_exc)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_credentials_async.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
"""Interfaces for credentials."""
|
| 17 |
+
|
| 18 |
+
import abc
|
| 19 |
+
import inspect
|
| 20 |
+
|
| 21 |
+
import six
|
| 22 |
+
|
| 23 |
+
from google.auth import credentials
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@six.add_metaclass(abc.ABCMeta)
|
| 27 |
+
class Credentials(credentials.Credentials):
|
| 28 |
+
"""Async inherited credentials class from google.auth.credentials.
|
| 29 |
+
The added functionality is the before_request call which requires
|
| 30 |
+
async/await syntax.
|
| 31 |
+
All credentials have a :attr:`token` that is used for authentication and
|
| 32 |
+
may also optionally set an :attr:`expiry` to indicate when the token will
|
| 33 |
+
no longer be valid.
|
| 34 |
+
|
| 35 |
+
Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
|
| 36 |
+
Credentials can do this automatically before the first HTTP request in
|
| 37 |
+
:meth:`before_request`.
|
| 38 |
+
|
| 39 |
+
Although the token and expiration will change as the credentials are
|
| 40 |
+
:meth:`refreshed <refresh>` and used, credentials should be considered
|
| 41 |
+
immutable. Various credentials will accept configuration such as private
|
| 42 |
+
keys, scopes, and other options. These options are not changeable after
|
| 43 |
+
construction. Some classes will provide mechanisms to copy the credentials
|
| 44 |
+
with modifications such as :meth:`ScopedCredentials.with_scopes`.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
async def before_request(self, request, method, url, headers):
|
| 48 |
+
"""Performs credential-specific before request logic.
|
| 49 |
+
|
| 50 |
+
Refreshes the credentials if necessary, then calls :meth:`apply` to
|
| 51 |
+
apply the token to the authentication header.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
request (google.auth.transport.Request): The object used to make
|
| 55 |
+
HTTP requests.
|
| 56 |
+
method (str): The request's HTTP method or the RPC method being
|
| 57 |
+
invoked.
|
| 58 |
+
url (str): The request's URI or the RPC service's URI.
|
| 59 |
+
headers (Mapping): The request's headers.
|
| 60 |
+
"""
|
| 61 |
+
# pylint: disable=unused-argument
|
| 62 |
+
# (Subclasses may use these arguments to ascertain information about
|
| 63 |
+
# the http request.)
|
| 64 |
+
|
| 65 |
+
if not self.valid:
|
| 66 |
+
if inspect.iscoroutinefunction(self.refresh):
|
| 67 |
+
await self.refresh(request)
|
| 68 |
+
else:
|
| 69 |
+
self.refresh(request)
|
| 70 |
+
self.apply(headers)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class CredentialsWithQuotaProject(credentials.CredentialsWithQuotaProject):
|
| 74 |
+
"""Abstract base for credentials supporting ``with_quota_project`` factory"""
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class AnonymousCredentials(credentials.AnonymousCredentials, Credentials):
|
| 78 |
+
"""Credentials that do not provide any authentication information.
|
| 79 |
+
|
| 80 |
+
These are useful in the case of services that support anonymous access or
|
| 81 |
+
local service emulators that do not use credentials. This class inherits
|
| 82 |
+
from the sync anonymous credentials file, but is kept if async credentials
|
| 83 |
+
is initialized and we would like anonymous credentials.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@six.add_metaclass(abc.ABCMeta)
|
| 88 |
+
class ReadOnlyScoped(credentials.ReadOnlyScoped):
|
| 89 |
+
"""Interface for credentials whose scopes can be queried.
|
| 90 |
+
|
| 91 |
+
OAuth 2.0-based credentials allow limiting access using scopes as described
|
| 92 |
+
in `RFC6749 Section 3.3`_.
|
| 93 |
+
If a credential class implements this interface then the credentials either
|
| 94 |
+
use scopes in their implementation.
|
| 95 |
+
|
| 96 |
+
Some credentials require scopes in order to obtain a token. You can check
|
| 97 |
+
if scoping is necessary with :attr:`requires_scopes`::
|
| 98 |
+
|
| 99 |
+
if credentials.requires_scopes:
|
| 100 |
+
# Scoping is required.
|
| 101 |
+
credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
|
| 102 |
+
|
| 103 |
+
Credentials that require scopes must either be constructed with scopes::
|
| 104 |
+
|
| 105 |
+
credentials = SomeScopedCredentials(scopes=['one', 'two'])
|
| 106 |
+
|
| 107 |
+
Or must copy an existing instance using :meth:`with_scopes`::
|
| 108 |
+
|
| 109 |
+
scoped_credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
|
| 110 |
+
|
| 111 |
+
Some credentials have scopes but do not allow or require scopes to be set,
|
| 112 |
+
these credentials can be used as-is.
|
| 113 |
+
|
| 114 |
+
.. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
class Scoped(credentials.Scoped):
|
| 119 |
+
"""Interface for credentials whose scopes can be replaced while copying.
|
| 120 |
+
|
| 121 |
+
OAuth 2.0-based credentials allow limiting access using scopes as described
|
| 122 |
+
in `RFC6749 Section 3.3`_.
|
| 123 |
+
If a credential class implements this interface then the credentials either
|
| 124 |
+
use scopes in their implementation.
|
| 125 |
+
|
| 126 |
+
Some credentials require scopes in order to obtain a token. You can check
|
| 127 |
+
if scoping is necessary with :attr:`requires_scopes`::
|
| 128 |
+
|
| 129 |
+
if credentials.requires_scopes:
|
| 130 |
+
# Scoping is required.
|
| 131 |
+
credentials = _credentials_async.create_scoped(['one', 'two'])
|
| 132 |
+
|
| 133 |
+
Credentials that require scopes must either be constructed with scopes::
|
| 134 |
+
|
| 135 |
+
credentials = SomeScopedCredentials(scopes=['one', 'two'])
|
| 136 |
+
|
| 137 |
+
Or must copy an existing instance using :meth:`with_scopes`::
|
| 138 |
+
|
| 139 |
+
scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
|
| 140 |
+
|
| 141 |
+
Some credentials have scopes but do not allow or require scopes to be set,
|
| 142 |
+
these credentials can be used as-is.
|
| 143 |
+
|
| 144 |
+
.. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def with_scopes_if_required(credentials, scopes):
|
| 149 |
+
"""Creates a copy of the credentials with scopes if scoping is required.
|
| 150 |
+
|
| 151 |
+
This helper function is useful when you do not know (or care to know) the
|
| 152 |
+
specific type of credentials you are using (such as when you use
|
| 153 |
+
:func:`google.auth.default`). This function will call
|
| 154 |
+
:meth:`Scoped.with_scopes` if the credentials are scoped credentials and if
|
| 155 |
+
the credentials require scoping. Otherwise, it will return the credentials
|
| 156 |
+
as-is.
|
| 157 |
+
|
| 158 |
+
Args:
|
| 159 |
+
credentials (google.auth.credentials.Credentials): The credentials to
|
| 160 |
+
scope if necessary.
|
| 161 |
+
scopes (Sequence[str]): The list of scopes to use.
|
| 162 |
+
|
| 163 |
+
Returns:
|
| 164 |
+
google.auth._credentials_async.Credentials: Either a new set of scoped
|
| 165 |
+
credentials, or the passed in credentials instance if no scoping
|
| 166 |
+
was required.
|
| 167 |
+
"""
|
| 168 |
+
if isinstance(credentials, Scoped) and credentials.requires_scopes:
|
| 169 |
+
return credentials.with_scopes(scopes)
|
| 170 |
+
else:
|
| 171 |
+
return credentials
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
@six.add_metaclass(abc.ABCMeta)
|
| 175 |
+
class Signing(credentials.Signing):
|
| 176 |
+
"""Interface for credentials that can cryptographically sign messages."""
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_helpers.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2015 Google Inc.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Helper functions for commonly used utilities."""
|
| 16 |
+
|
| 17 |
+
import base64
|
| 18 |
+
import calendar
|
| 19 |
+
import datetime
|
| 20 |
+
import sys
|
| 21 |
+
|
| 22 |
+
import six
|
| 23 |
+
from six.moves import urllib
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# Token server doesn't provide a new a token when doing refresh unless the
|
| 27 |
+
# token is expiring within 30 seconds, so refresh threshold should not be
|
| 28 |
+
# more than 30 seconds. Otherwise auth lib will send tons of refresh requests
|
| 29 |
+
# until 30 seconds before the expiration, and cause a spike of CPU usage.
|
| 30 |
+
REFRESH_THRESHOLD = datetime.timedelta(seconds=20)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def copy_docstring(source_class):
|
| 34 |
+
"""Decorator that copies a method's docstring from another class.
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
source_class (type): The class that has the documented method.
|
| 38 |
+
|
| 39 |
+
Returns:
|
| 40 |
+
Callable: A decorator that will copy the docstring of the same
|
| 41 |
+
named method in the source class to the decorated method.
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
+
def decorator(method):
|
| 45 |
+
"""Decorator implementation.
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
method (Callable): The method to copy the docstring to.
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
Callable: the same method passed in with an updated docstring.
|
| 52 |
+
|
| 53 |
+
Raises:
|
| 54 |
+
ValueError: if the method already has a docstring.
|
| 55 |
+
"""
|
| 56 |
+
if method.__doc__:
|
| 57 |
+
raise ValueError("Method already has a docstring.")
|
| 58 |
+
|
| 59 |
+
source_method = getattr(source_class, method.__name__)
|
| 60 |
+
method.__doc__ = source_method.__doc__
|
| 61 |
+
|
| 62 |
+
return method
|
| 63 |
+
|
| 64 |
+
return decorator
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def utcnow():
|
| 68 |
+
"""Returns the current UTC datetime.
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
datetime: The current time in UTC.
|
| 72 |
+
"""
|
| 73 |
+
return datetime.datetime.utcnow()
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def datetime_to_secs(value):
|
| 77 |
+
"""Convert a datetime object to the number of seconds since the UNIX epoch.
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
value (datetime): The datetime to convert.
|
| 81 |
+
|
| 82 |
+
Returns:
|
| 83 |
+
int: The number of seconds since the UNIX epoch.
|
| 84 |
+
"""
|
| 85 |
+
return calendar.timegm(value.utctimetuple())
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def to_bytes(value, encoding="utf-8"):
|
| 89 |
+
"""Converts a string value to bytes, if necessary.
|
| 90 |
+
|
| 91 |
+
Unfortunately, ``six.b`` is insufficient for this task since in
|
| 92 |
+
Python 2 because it does not modify ``unicode`` objects.
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
value (Union[str, bytes]): The value to be converted.
|
| 96 |
+
encoding (str): The encoding to use to convert unicode to bytes.
|
| 97 |
+
Defaults to "utf-8".
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
bytes: The original value converted to bytes (if unicode) or as
|
| 101 |
+
passed in if it started out as bytes.
|
| 102 |
+
|
| 103 |
+
Raises:
|
| 104 |
+
ValueError: If the value could not be converted to bytes.
|
| 105 |
+
"""
|
| 106 |
+
result = value.encode(encoding) if isinstance(value, six.text_type) else value
|
| 107 |
+
if isinstance(result, six.binary_type):
|
| 108 |
+
return result
|
| 109 |
+
else:
|
| 110 |
+
raise ValueError("{0!r} could not be converted to bytes".format(value))
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def from_bytes(value):
|
| 114 |
+
"""Converts bytes to a string value, if necessary.
|
| 115 |
+
|
| 116 |
+
Args:
|
| 117 |
+
value (Union[str, bytes]): The value to be converted.
|
| 118 |
+
|
| 119 |
+
Returns:
|
| 120 |
+
str: The original value converted to unicode (if bytes) or as passed in
|
| 121 |
+
if it started out as unicode.
|
| 122 |
+
|
| 123 |
+
Raises:
|
| 124 |
+
ValueError: If the value could not be converted to unicode.
|
| 125 |
+
"""
|
| 126 |
+
result = value.decode("utf-8") if isinstance(value, six.binary_type) else value
|
| 127 |
+
if isinstance(result, six.text_type):
|
| 128 |
+
return result
|
| 129 |
+
else:
|
| 130 |
+
raise ValueError("{0!r} could not be converted to unicode".format(value))
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def update_query(url, params, remove=None):
|
| 134 |
+
"""Updates a URL's query parameters.
|
| 135 |
+
|
| 136 |
+
Replaces any current values if they are already present in the URL.
|
| 137 |
+
|
| 138 |
+
Args:
|
| 139 |
+
url (str): The URL to update.
|
| 140 |
+
params (Mapping[str, str]): A mapping of query parameter
|
| 141 |
+
keys to values.
|
| 142 |
+
remove (Sequence[str]): Parameters to remove from the query string.
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
str: The URL with updated query parameters.
|
| 146 |
+
|
| 147 |
+
Examples:
|
| 148 |
+
|
| 149 |
+
>>> url = 'http://example.com?a=1'
|
| 150 |
+
>>> update_query(url, {'a': '2'})
|
| 151 |
+
http://example.com?a=2
|
| 152 |
+
>>> update_query(url, {'b': '3'})
|
| 153 |
+
http://example.com?a=1&b=3
|
| 154 |
+
>> update_query(url, {'b': '3'}, remove=['a'])
|
| 155 |
+
http://example.com?b=3
|
| 156 |
+
|
| 157 |
+
"""
|
| 158 |
+
if remove is None:
|
| 159 |
+
remove = []
|
| 160 |
+
|
| 161 |
+
# Split the URL into parts.
|
| 162 |
+
parts = urllib.parse.urlparse(url)
|
| 163 |
+
# Parse the query string.
|
| 164 |
+
query_params = urllib.parse.parse_qs(parts.query)
|
| 165 |
+
# Update the query parameters with the new parameters.
|
| 166 |
+
query_params.update(params)
|
| 167 |
+
# Remove any values specified in remove.
|
| 168 |
+
query_params = {
|
| 169 |
+
key: value for key, value in six.iteritems(query_params) if key not in remove
|
| 170 |
+
}
|
| 171 |
+
# Re-encoded the query string.
|
| 172 |
+
new_query = urllib.parse.urlencode(query_params, doseq=True)
|
| 173 |
+
# Unsplit the url.
|
| 174 |
+
new_parts = parts._replace(query=new_query)
|
| 175 |
+
return urllib.parse.urlunparse(new_parts)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def scopes_to_string(scopes):
|
| 179 |
+
"""Converts scope value to a string suitable for sending to OAuth 2.0
|
| 180 |
+
authorization servers.
|
| 181 |
+
|
| 182 |
+
Args:
|
| 183 |
+
scopes (Sequence[str]): The sequence of scopes to convert.
|
| 184 |
+
|
| 185 |
+
Returns:
|
| 186 |
+
str: The scopes formatted as a single string.
|
| 187 |
+
"""
|
| 188 |
+
return " ".join(scopes)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def string_to_scopes(scopes):
|
| 192 |
+
"""Converts stringifed scopes value to a list.
|
| 193 |
+
|
| 194 |
+
Args:
|
| 195 |
+
scopes (Union[Sequence, str]): The string of space-separated scopes
|
| 196 |
+
to convert.
|
| 197 |
+
Returns:
|
| 198 |
+
Sequence(str): The separated scopes.
|
| 199 |
+
"""
|
| 200 |
+
if not scopes:
|
| 201 |
+
return []
|
| 202 |
+
|
| 203 |
+
return scopes.split(" ")
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def padded_urlsafe_b64decode(value):
|
| 207 |
+
"""Decodes base64 strings lacking padding characters.
|
| 208 |
+
|
| 209 |
+
Google infrastructure tends to omit the base64 padding characters.
|
| 210 |
+
|
| 211 |
+
Args:
|
| 212 |
+
value (Union[str, bytes]): The encoded value.
|
| 213 |
+
|
| 214 |
+
Returns:
|
| 215 |
+
bytes: The decoded value
|
| 216 |
+
"""
|
| 217 |
+
b64string = to_bytes(value)
|
| 218 |
+
padded = b64string + b"=" * (-len(b64string) % 4)
|
| 219 |
+
return base64.urlsafe_b64decode(padded)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def unpadded_urlsafe_b64encode(value):
|
| 223 |
+
"""Encodes base64 strings removing any padding characters.
|
| 224 |
+
|
| 225 |
+
`rfc 7515`_ defines Base64url to NOT include any padding
|
| 226 |
+
characters, but the stdlib doesn't do that by default.
|
| 227 |
+
|
| 228 |
+
_rfc7515: https://tools.ietf.org/html/rfc7515#page-6
|
| 229 |
+
|
| 230 |
+
Args:
|
| 231 |
+
value (Union[str|bytes]): The bytes-like value to encode
|
| 232 |
+
|
| 233 |
+
Returns:
|
| 234 |
+
Union[str|bytes]: The encoded value
|
| 235 |
+
"""
|
| 236 |
+
return base64.urlsafe_b64encode(value).rstrip(b"=")
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def is_python_3():
|
| 240 |
+
"""Check if the Python interpreter is Python 2 or 3.
|
| 241 |
+
|
| 242 |
+
Returns:
|
| 243 |
+
bool: True if the Python interpreter is Python 3 and False otherwise.
|
| 244 |
+
"""
|
| 245 |
+
return sys.version_info > (3, 0)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/_service_account_info.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Helper functions for loading data from a Google service account file."""
|
| 16 |
+
|
| 17 |
+
import io
|
| 18 |
+
import json
|
| 19 |
+
|
| 20 |
+
import six
|
| 21 |
+
|
| 22 |
+
from google.auth import crypt
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def from_dict(data, require=None, use_rsa_signer=True):
|
| 26 |
+
"""Validates a dictionary containing Google service account data.
|
| 27 |
+
|
| 28 |
+
Creates and returns a :class:`google.auth.crypt.Signer` instance from the
|
| 29 |
+
private key specified in the data.
|
| 30 |
+
|
| 31 |
+
Args:
|
| 32 |
+
data (Mapping[str, str]): The service account data
|
| 33 |
+
require (Sequence[str]): List of keys required to be present in the
|
| 34 |
+
info.
|
| 35 |
+
use_rsa_signer (Optional[bool]): Whether to use RSA signer or EC signer.
|
| 36 |
+
We use RSA signer by default.
|
| 37 |
+
|
| 38 |
+
Returns:
|
| 39 |
+
google.auth.crypt.Signer: A signer created from the private key in the
|
| 40 |
+
service account file.
|
| 41 |
+
|
| 42 |
+
Raises:
|
| 43 |
+
ValueError: if the data was in the wrong format, or if one of the
|
| 44 |
+
required keys is missing.
|
| 45 |
+
"""
|
| 46 |
+
keys_needed = set(require if require is not None else [])
|
| 47 |
+
|
| 48 |
+
missing = keys_needed.difference(six.iterkeys(data))
|
| 49 |
+
|
| 50 |
+
if missing:
|
| 51 |
+
raise ValueError(
|
| 52 |
+
"Service account info was not in the expected format, missing "
|
| 53 |
+
"fields {}.".format(", ".join(missing))
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
# Create a signer.
|
| 57 |
+
if use_rsa_signer:
|
| 58 |
+
signer = crypt.RSASigner.from_service_account_info(data)
|
| 59 |
+
else:
|
| 60 |
+
signer = crypt.ES256Signer.from_service_account_info(data)
|
| 61 |
+
|
| 62 |
+
return signer
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def from_filename(filename, require=None, use_rsa_signer=True):
|
| 66 |
+
"""Reads a Google service account JSON file and returns its parsed info.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
filename (str): The path to the service account .json file.
|
| 70 |
+
require (Sequence[str]): List of keys required to be present in the
|
| 71 |
+
info.
|
| 72 |
+
use_rsa_signer (Optional[bool]): Whether to use RSA signer or EC signer.
|
| 73 |
+
We use RSA signer by default.
|
| 74 |
+
|
| 75 |
+
Returns:
|
| 76 |
+
Tuple[ Mapping[str, str], google.auth.crypt.Signer ]: The verified
|
| 77 |
+
info and a signer instance.
|
| 78 |
+
"""
|
| 79 |
+
with io.open(filename, "r", encoding="utf-8") as json_file:
|
| 80 |
+
data = json.load(json_file)
|
| 81 |
+
return data, from_dict(data, require=require, use_rsa_signer=use_rsa_signer)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/app_engine.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Google App Engine standard environment support.
|
| 16 |
+
|
| 17 |
+
This module provides authentication and signing for applications running on App
|
| 18 |
+
Engine in the standard environment using the `App Identity API`_.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
.. _App Identity API:
|
| 22 |
+
https://cloud.google.com/appengine/docs/python/appidentity/
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
import datetime
|
| 26 |
+
|
| 27 |
+
from google.auth import _helpers
|
| 28 |
+
from google.auth import credentials
|
| 29 |
+
from google.auth import crypt
|
| 30 |
+
|
| 31 |
+
# pytype: disable=import-error
|
| 32 |
+
try:
|
| 33 |
+
from google.appengine.api import app_identity # type: ignore
|
| 34 |
+
except ImportError:
|
| 35 |
+
app_identity = None # type: ignore
|
| 36 |
+
# pytype: enable=import-error
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Signer(crypt.Signer):
|
| 40 |
+
"""Signs messages using the App Engine App Identity service.
|
| 41 |
+
|
| 42 |
+
This can be used in place of :class:`google.auth.crypt.Signer` when
|
| 43 |
+
running in the App Engine standard environment.
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
@property
|
| 47 |
+
def key_id(self):
|
| 48 |
+
"""Optional[str]: The key ID used to identify this private key.
|
| 49 |
+
|
| 50 |
+
.. warning::
|
| 51 |
+
This is always ``None``. The key ID used by App Engine can not
|
| 52 |
+
be reliably determined ahead of time.
|
| 53 |
+
"""
|
| 54 |
+
return None
|
| 55 |
+
|
| 56 |
+
@_helpers.copy_docstring(crypt.Signer)
|
| 57 |
+
def sign(self, message):
|
| 58 |
+
message = _helpers.to_bytes(message)
|
| 59 |
+
_, signature = app_identity.sign_blob(message)
|
| 60 |
+
return signature
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def get_project_id():
|
| 64 |
+
"""Gets the project ID for the current App Engine application.
|
| 65 |
+
|
| 66 |
+
Returns:
|
| 67 |
+
str: The project ID
|
| 68 |
+
|
| 69 |
+
Raises:
|
| 70 |
+
EnvironmentError: If the App Engine APIs are unavailable.
|
| 71 |
+
"""
|
| 72 |
+
# pylint: disable=missing-raises-doc
|
| 73 |
+
# Pylint rightfully thinks EnvironmentError is OSError, but doesn't
|
| 74 |
+
# realize it's a valid alias.
|
| 75 |
+
if app_identity is None:
|
| 76 |
+
raise EnvironmentError("The App Engine APIs are not available.")
|
| 77 |
+
return app_identity.get_application_id()
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class Credentials(
|
| 81 |
+
credentials.Scoped, credentials.Signing, credentials.CredentialsWithQuotaProject
|
| 82 |
+
):
|
| 83 |
+
"""App Engine standard environment credentials.
|
| 84 |
+
|
| 85 |
+
These credentials use the App Engine App Identity API to obtain access
|
| 86 |
+
tokens.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
def __init__(
|
| 90 |
+
self,
|
| 91 |
+
scopes=None,
|
| 92 |
+
default_scopes=None,
|
| 93 |
+
service_account_id=None,
|
| 94 |
+
quota_project_id=None,
|
| 95 |
+
):
|
| 96 |
+
"""
|
| 97 |
+
Args:
|
| 98 |
+
scopes (Sequence[str]): Scopes to request from the App Identity
|
| 99 |
+
API.
|
| 100 |
+
default_scopes (Sequence[str]): Default scopes passed by a
|
| 101 |
+
Google client library. Use 'scopes' for user-defined scopes.
|
| 102 |
+
service_account_id (str): The service account ID passed into
|
| 103 |
+
:func:`google.appengine.api.app_identity.get_access_token`.
|
| 104 |
+
If not specified, the default application service account
|
| 105 |
+
ID will be used.
|
| 106 |
+
quota_project_id (Optional[str]): The project ID used for quota
|
| 107 |
+
and billing.
|
| 108 |
+
|
| 109 |
+
Raises:
|
| 110 |
+
EnvironmentError: If the App Engine APIs are unavailable.
|
| 111 |
+
"""
|
| 112 |
+
# pylint: disable=missing-raises-doc
|
| 113 |
+
# Pylint rightfully thinks EnvironmentError is OSError, but doesn't
|
| 114 |
+
# realize it's a valid alias.
|
| 115 |
+
if app_identity is None:
|
| 116 |
+
raise EnvironmentError("The App Engine APIs are not available.")
|
| 117 |
+
|
| 118 |
+
super(Credentials, self).__init__()
|
| 119 |
+
self._scopes = scopes
|
| 120 |
+
self._default_scopes = default_scopes
|
| 121 |
+
self._service_account_id = service_account_id
|
| 122 |
+
self._signer = Signer()
|
| 123 |
+
self._quota_project_id = quota_project_id
|
| 124 |
+
|
| 125 |
+
@_helpers.copy_docstring(credentials.Credentials)
|
| 126 |
+
def refresh(self, request):
|
| 127 |
+
scopes = self._scopes if self._scopes is not None else self._default_scopes
|
| 128 |
+
# pylint: disable=unused-argument
|
| 129 |
+
token, ttl = app_identity.get_access_token(scopes, self._service_account_id)
|
| 130 |
+
expiry = datetime.datetime.utcfromtimestamp(ttl)
|
| 131 |
+
|
| 132 |
+
self.token, self.expiry = token, expiry
|
| 133 |
+
|
| 134 |
+
@property
|
| 135 |
+
def service_account_email(self):
|
| 136 |
+
"""The service account email."""
|
| 137 |
+
if self._service_account_id is None:
|
| 138 |
+
self._service_account_id = app_identity.get_service_account_name()
|
| 139 |
+
return self._service_account_id
|
| 140 |
+
|
| 141 |
+
@property
|
| 142 |
+
def requires_scopes(self):
|
| 143 |
+
"""Checks if the credentials requires scopes.
|
| 144 |
+
|
| 145 |
+
Returns:
|
| 146 |
+
bool: True if there are no scopes set otherwise False.
|
| 147 |
+
"""
|
| 148 |
+
return not self._scopes and not self._default_scopes
|
| 149 |
+
|
| 150 |
+
@_helpers.copy_docstring(credentials.Scoped)
|
| 151 |
+
def with_scopes(self, scopes, default_scopes=None):
|
| 152 |
+
return self.__class__(
|
| 153 |
+
scopes=scopes,
|
| 154 |
+
default_scopes=default_scopes,
|
| 155 |
+
service_account_id=self._service_account_id,
|
| 156 |
+
quota_project_id=self.quota_project_id,
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
|
| 160 |
+
def with_quota_project(self, quota_project_id):
|
| 161 |
+
return self.__class__(
|
| 162 |
+
scopes=self._scopes,
|
| 163 |
+
service_account_id=self._service_account_id,
|
| 164 |
+
quota_project_id=quota_project_id,
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
@_helpers.copy_docstring(credentials.Signing)
|
| 168 |
+
def sign_bytes(self, message):
|
| 169 |
+
return self._signer.sign(message)
|
| 170 |
+
|
| 171 |
+
@property # type: ignore
|
| 172 |
+
@_helpers.copy_docstring(credentials.Signing)
|
| 173 |
+
def signer_email(self):
|
| 174 |
+
return self.service_account_email
|
| 175 |
+
|
| 176 |
+
@property # type: ignore
|
| 177 |
+
@_helpers.copy_docstring(credentials.Signing)
|
| 178 |
+
def signer(self):
|
| 179 |
+
return self._signer
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/jwt.py
ADDED
|
@@ -0,0 +1,857 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""JSON Web Tokens
|
| 16 |
+
|
| 17 |
+
Provides support for creating (encoding) and verifying (decoding) JWTs,
|
| 18 |
+
especially JWTs generated and consumed by Google infrastructure.
|
| 19 |
+
|
| 20 |
+
See `rfc7519`_ for more details on JWTs.
|
| 21 |
+
|
| 22 |
+
To encode a JWT use :func:`encode`::
|
| 23 |
+
|
| 24 |
+
from google.auth import crypt
|
| 25 |
+
from google.auth import jwt
|
| 26 |
+
|
| 27 |
+
signer = crypt.Signer(private_key)
|
| 28 |
+
payload = {'some': 'payload'}
|
| 29 |
+
encoded = jwt.encode(signer, payload)
|
| 30 |
+
|
| 31 |
+
To decode a JWT and verify claims use :func:`decode`::
|
| 32 |
+
|
| 33 |
+
claims = jwt.decode(encoded, certs=public_certs)
|
| 34 |
+
|
| 35 |
+
You can also skip verification::
|
| 36 |
+
|
| 37 |
+
claims = jwt.decode(encoded, verify=False)
|
| 38 |
+
|
| 39 |
+
.. _rfc7519: https://tools.ietf.org/html/rfc7519
|
| 40 |
+
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
from collections.abc import Mapping
|
| 45 |
+
# Python 2.7 compatibility
|
| 46 |
+
except ImportError: # pragma: NO COVER
|
| 47 |
+
from collections import Mapping # type: ignore
|
| 48 |
+
import copy
|
| 49 |
+
import datetime
|
| 50 |
+
import json
|
| 51 |
+
|
| 52 |
+
import cachetools
|
| 53 |
+
import six
|
| 54 |
+
from six.moves import urllib
|
| 55 |
+
|
| 56 |
+
from google.auth import _helpers
|
| 57 |
+
from google.auth import _service_account_info
|
| 58 |
+
from google.auth import crypt
|
| 59 |
+
from google.auth import exceptions
|
| 60 |
+
import google.auth.credentials
|
| 61 |
+
|
| 62 |
+
try:
|
| 63 |
+
from google.auth.crypt import es256
|
| 64 |
+
except ImportError: # pragma: NO COVER
|
| 65 |
+
es256 = None # type: ignore
|
| 66 |
+
|
| 67 |
+
_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
| 68 |
+
_DEFAULT_MAX_CACHE_SIZE = 10
|
| 69 |
+
_ALGORITHM_TO_VERIFIER_CLASS = {"RS256": crypt.RSAVerifier}
|
| 70 |
+
_CRYPTOGRAPHY_BASED_ALGORITHMS = frozenset(["ES256"])
|
| 71 |
+
|
| 72 |
+
if es256 is not None: # pragma: NO COVER
|
| 73 |
+
_ALGORITHM_TO_VERIFIER_CLASS["ES256"] = es256.ES256Verifier # type: ignore
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def encode(signer, payload, header=None, key_id=None):
|
| 77 |
+
"""Make a signed JWT.
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
signer (google.auth.crypt.Signer): The signer used to sign the JWT.
|
| 81 |
+
payload (Mapping[str, str]): The JWT payload.
|
| 82 |
+
header (Mapping[str, str]): Additional JWT header payload.
|
| 83 |
+
key_id (str): The key id to add to the JWT header. If the
|
| 84 |
+
signer has a key id it will be used as the default. If this is
|
| 85 |
+
specified it will override the signer's key id.
|
| 86 |
+
|
| 87 |
+
Returns:
|
| 88 |
+
bytes: The encoded JWT.
|
| 89 |
+
"""
|
| 90 |
+
if header is None:
|
| 91 |
+
header = {}
|
| 92 |
+
|
| 93 |
+
if key_id is None:
|
| 94 |
+
key_id = signer.key_id
|
| 95 |
+
|
| 96 |
+
header.update({"typ": "JWT"})
|
| 97 |
+
|
| 98 |
+
if "alg" not in header:
|
| 99 |
+
if es256 is not None and isinstance(signer, es256.ES256Signer):
|
| 100 |
+
header.update({"alg": "ES256"})
|
| 101 |
+
else:
|
| 102 |
+
header.update({"alg": "RS256"})
|
| 103 |
+
|
| 104 |
+
if key_id is not None:
|
| 105 |
+
header["kid"] = key_id
|
| 106 |
+
|
| 107 |
+
segments = [
|
| 108 |
+
_helpers.unpadded_urlsafe_b64encode(json.dumps(header).encode("utf-8")),
|
| 109 |
+
_helpers.unpadded_urlsafe_b64encode(json.dumps(payload).encode("utf-8")),
|
| 110 |
+
]
|
| 111 |
+
|
| 112 |
+
signing_input = b".".join(segments)
|
| 113 |
+
signature = signer.sign(signing_input)
|
| 114 |
+
segments.append(_helpers.unpadded_urlsafe_b64encode(signature))
|
| 115 |
+
|
| 116 |
+
return b".".join(segments)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def _decode_jwt_segment(encoded_section):
|
| 120 |
+
"""Decodes a single JWT segment."""
|
| 121 |
+
section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section)
|
| 122 |
+
try:
|
| 123 |
+
return json.loads(section_bytes.decode("utf-8"))
|
| 124 |
+
except ValueError as caught_exc:
|
| 125 |
+
new_exc = ValueError("Can't parse segment: {0}".format(section_bytes))
|
| 126 |
+
six.raise_from(new_exc, caught_exc)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def _unverified_decode(token):
|
| 130 |
+
"""Decodes a token and does no verification.
|
| 131 |
+
|
| 132 |
+
Args:
|
| 133 |
+
token (Union[str, bytes]): The encoded JWT.
|
| 134 |
+
|
| 135 |
+
Returns:
|
| 136 |
+
Tuple[str, str, str, str]: header, payload, signed_section, and
|
| 137 |
+
signature.
|
| 138 |
+
|
| 139 |
+
Raises:
|
| 140 |
+
ValueError: if there are an incorrect amount of segments in the token.
|
| 141 |
+
"""
|
| 142 |
+
token = _helpers.to_bytes(token)
|
| 143 |
+
|
| 144 |
+
if token.count(b".") != 2:
|
| 145 |
+
raise ValueError("Wrong number of segments in token: {0}".format(token))
|
| 146 |
+
|
| 147 |
+
encoded_header, encoded_payload, signature = token.split(b".")
|
| 148 |
+
signed_section = encoded_header + b"." + encoded_payload
|
| 149 |
+
signature = _helpers.padded_urlsafe_b64decode(signature)
|
| 150 |
+
|
| 151 |
+
# Parse segments
|
| 152 |
+
header = _decode_jwt_segment(encoded_header)
|
| 153 |
+
payload = _decode_jwt_segment(encoded_payload)
|
| 154 |
+
|
| 155 |
+
return header, payload, signed_section, signature
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def decode_header(token):
|
| 159 |
+
"""Return the decoded header of a token.
|
| 160 |
+
|
| 161 |
+
No verification is done. This is useful to extract the key id from
|
| 162 |
+
the header in order to acquire the appropriate certificate to verify
|
| 163 |
+
the token.
|
| 164 |
+
|
| 165 |
+
Args:
|
| 166 |
+
token (Union[str, bytes]): the encoded JWT.
|
| 167 |
+
|
| 168 |
+
Returns:
|
| 169 |
+
Mapping: The decoded JWT header.
|
| 170 |
+
"""
|
| 171 |
+
header, _, _, _ = _unverified_decode(token)
|
| 172 |
+
return header
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def _verify_iat_and_exp(payload, clock_skew_in_seconds=0):
|
| 176 |
+
"""Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token
|
| 177 |
+
payload.
|
| 178 |
+
|
| 179 |
+
Args:
|
| 180 |
+
payload (Mapping[str, str]): The JWT payload.
|
| 181 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 182 |
+
validation.
|
| 183 |
+
|
| 184 |
+
Raises:
|
| 185 |
+
ValueError: if any checks failed.
|
| 186 |
+
"""
|
| 187 |
+
now = _helpers.datetime_to_secs(_helpers.utcnow())
|
| 188 |
+
|
| 189 |
+
# Make sure the iat and exp claims are present.
|
| 190 |
+
for key in ("iat", "exp"):
|
| 191 |
+
if key not in payload:
|
| 192 |
+
raise ValueError("Token does not contain required claim {}".format(key))
|
| 193 |
+
|
| 194 |
+
# Make sure the token wasn't issued in the future.
|
| 195 |
+
iat = payload["iat"]
|
| 196 |
+
# Err on the side of accepting a token that is slightly early to account
|
| 197 |
+
# for clock skew.
|
| 198 |
+
earliest = iat - clock_skew_in_seconds
|
| 199 |
+
if now < earliest:
|
| 200 |
+
raise ValueError(
|
| 201 |
+
"Token used too early, {} < {}. Check that your computer's clock is set correctly.".format(
|
| 202 |
+
now, iat
|
| 203 |
+
)
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
# Make sure the token wasn't issued in the past.
|
| 207 |
+
exp = payload["exp"]
|
| 208 |
+
# Err on the side of accepting a token that is slightly out of date
|
| 209 |
+
# to account for clow skew.
|
| 210 |
+
latest = exp + clock_skew_in_seconds
|
| 211 |
+
if latest < now:
|
| 212 |
+
raise ValueError("Token expired, {} < {}".format(latest, now))
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def decode(token, certs=None, verify=True, audience=None, clock_skew_in_seconds=0):
|
| 216 |
+
"""Decode and verify a JWT.
|
| 217 |
+
|
| 218 |
+
Args:
|
| 219 |
+
token (str): The encoded JWT.
|
| 220 |
+
certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The
|
| 221 |
+
certificate used to validate the JWT signature. If bytes or string,
|
| 222 |
+
it must the the public key certificate in PEM format. If a mapping,
|
| 223 |
+
it must be a mapping of key IDs to public key certificates in PEM
|
| 224 |
+
format. The mapping must contain the same key ID that's specified
|
| 225 |
+
in the token's header.
|
| 226 |
+
verify (bool): Whether to perform signature and claim validation.
|
| 227 |
+
Verification is done by default.
|
| 228 |
+
audience (str or list): The audience claim, 'aud', that this JWT should
|
| 229 |
+
contain. Or a list of audience claims. If None then the JWT's 'aud'
|
| 230 |
+
parameter is not verified.
|
| 231 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 232 |
+
validation.
|
| 233 |
+
|
| 234 |
+
Returns:
|
| 235 |
+
Mapping[str, str]: The deserialized JSON payload in the JWT.
|
| 236 |
+
|
| 237 |
+
Raises:
|
| 238 |
+
ValueError: if any verification checks failed.
|
| 239 |
+
"""
|
| 240 |
+
header, payload, signed_section, signature = _unverified_decode(token)
|
| 241 |
+
|
| 242 |
+
if not verify:
|
| 243 |
+
return payload
|
| 244 |
+
|
| 245 |
+
# Pluck the key id and algorithm from the header and make sure we have
|
| 246 |
+
# a verifier that can support it.
|
| 247 |
+
key_alg = header.get("alg")
|
| 248 |
+
key_id = header.get("kid")
|
| 249 |
+
|
| 250 |
+
try:
|
| 251 |
+
verifier_cls = _ALGORITHM_TO_VERIFIER_CLASS[key_alg]
|
| 252 |
+
except KeyError as exc:
|
| 253 |
+
if key_alg in _CRYPTOGRAPHY_BASED_ALGORITHMS:
|
| 254 |
+
six.raise_from(
|
| 255 |
+
ValueError(
|
| 256 |
+
"The key algorithm {} requires the cryptography package "
|
| 257 |
+
"to be installed.".format(key_alg)
|
| 258 |
+
),
|
| 259 |
+
exc,
|
| 260 |
+
)
|
| 261 |
+
else:
|
| 262 |
+
six.raise_from(
|
| 263 |
+
ValueError("Unsupported signature algorithm {}".format(key_alg)), exc
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
# If certs is specified as a dictionary of key IDs to certificates, then
|
| 267 |
+
# use the certificate identified by the key ID in the token header.
|
| 268 |
+
if isinstance(certs, Mapping):
|
| 269 |
+
if key_id:
|
| 270 |
+
if key_id not in certs:
|
| 271 |
+
raise ValueError("Certificate for key id {} not found.".format(key_id))
|
| 272 |
+
certs_to_check = [certs[key_id]]
|
| 273 |
+
# If there's no key id in the header, check against all of the certs.
|
| 274 |
+
else:
|
| 275 |
+
certs_to_check = certs.values()
|
| 276 |
+
else:
|
| 277 |
+
certs_to_check = certs
|
| 278 |
+
|
| 279 |
+
# Verify that the signature matches the message.
|
| 280 |
+
if not crypt.verify_signature(
|
| 281 |
+
signed_section, signature, certs_to_check, verifier_cls
|
| 282 |
+
):
|
| 283 |
+
raise ValueError("Could not verify token signature.")
|
| 284 |
+
|
| 285 |
+
# Verify the issued at and created times in the payload.
|
| 286 |
+
_verify_iat_and_exp(payload, clock_skew_in_seconds)
|
| 287 |
+
|
| 288 |
+
# Check audience.
|
| 289 |
+
if audience is not None:
|
| 290 |
+
claim_audience = payload.get("aud")
|
| 291 |
+
if isinstance(audience, str):
|
| 292 |
+
audience = [audience]
|
| 293 |
+
if claim_audience not in audience:
|
| 294 |
+
raise ValueError(
|
| 295 |
+
"Token has wrong audience {}, expected one of {}".format(
|
| 296 |
+
claim_audience, audience
|
| 297 |
+
)
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
return payload
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
class Credentials(
|
| 304 |
+
google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
|
| 305 |
+
):
|
| 306 |
+
"""Credentials that use a JWT as the bearer token.
|
| 307 |
+
|
| 308 |
+
These credentials require an "audience" claim. This claim identifies the
|
| 309 |
+
intended recipient of the bearer token.
|
| 310 |
+
|
| 311 |
+
The constructor arguments determine the claims for the JWT that is
|
| 312 |
+
sent with requests. Usually, you'll construct these credentials with
|
| 313 |
+
one of the helper constructors as shown in the next section.
|
| 314 |
+
|
| 315 |
+
To create JWT credentials using a Google service account private key
|
| 316 |
+
JSON file::
|
| 317 |
+
|
| 318 |
+
audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher'
|
| 319 |
+
credentials = jwt.Credentials.from_service_account_file(
|
| 320 |
+
'service-account.json',
|
| 321 |
+
audience=audience)
|
| 322 |
+
|
| 323 |
+
If you already have the service account file loaded and parsed::
|
| 324 |
+
|
| 325 |
+
service_account_info = json.load(open('service_account.json'))
|
| 326 |
+
credentials = jwt.Credentials.from_service_account_info(
|
| 327 |
+
service_account_info,
|
| 328 |
+
audience=audience)
|
| 329 |
+
|
| 330 |
+
Both helper methods pass on arguments to the constructor, so you can
|
| 331 |
+
specify the JWT claims::
|
| 332 |
+
|
| 333 |
+
credentials = jwt.Credentials.from_service_account_file(
|
| 334 |
+
'service-account.json',
|
| 335 |
+
audience=audience,
|
| 336 |
+
additional_claims={'meta': 'data'})
|
| 337 |
+
|
| 338 |
+
You can also construct the credentials directly if you have a
|
| 339 |
+
:class:`~google.auth.crypt.Signer` instance::
|
| 340 |
+
|
| 341 |
+
credentials = jwt.Credentials(
|
| 342 |
+
signer,
|
| 343 |
+
issuer='your-issuer',
|
| 344 |
+
subject='your-subject',
|
| 345 |
+
audience=audience)
|
| 346 |
+
|
| 347 |
+
The claims are considered immutable. If you want to modify the claims,
|
| 348 |
+
you can easily create another instance using :meth:`with_claims`::
|
| 349 |
+
|
| 350 |
+
new_audience = (
|
| 351 |
+
'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber')
|
| 352 |
+
new_credentials = credentials.with_claims(audience=new_audience)
|
| 353 |
+
"""
|
| 354 |
+
|
| 355 |
+
def __init__(
|
| 356 |
+
self,
|
| 357 |
+
signer,
|
| 358 |
+
issuer,
|
| 359 |
+
subject,
|
| 360 |
+
audience,
|
| 361 |
+
additional_claims=None,
|
| 362 |
+
token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
|
| 363 |
+
quota_project_id=None,
|
| 364 |
+
):
|
| 365 |
+
"""
|
| 366 |
+
Args:
|
| 367 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 368 |
+
issuer (str): The `iss` claim.
|
| 369 |
+
subject (str): The `sub` claim.
|
| 370 |
+
audience (str): the `aud` claim. The intended audience for the
|
| 371 |
+
credentials.
|
| 372 |
+
additional_claims (Mapping[str, str]): Any additional claims for
|
| 373 |
+
the JWT payload.
|
| 374 |
+
token_lifetime (int): The amount of time in seconds for
|
| 375 |
+
which the token is valid. Defaults to 1 hour.
|
| 376 |
+
quota_project_id (Optional[str]): The project ID used for quota
|
| 377 |
+
and billing.
|
| 378 |
+
"""
|
| 379 |
+
super(Credentials, self).__init__()
|
| 380 |
+
self._signer = signer
|
| 381 |
+
self._issuer = issuer
|
| 382 |
+
self._subject = subject
|
| 383 |
+
self._audience = audience
|
| 384 |
+
self._token_lifetime = token_lifetime
|
| 385 |
+
self._quota_project_id = quota_project_id
|
| 386 |
+
|
| 387 |
+
if additional_claims is None:
|
| 388 |
+
additional_claims = {}
|
| 389 |
+
|
| 390 |
+
self._additional_claims = additional_claims
|
| 391 |
+
|
| 392 |
+
@classmethod
|
| 393 |
+
def _from_signer_and_info(cls, signer, info, **kwargs):
|
| 394 |
+
"""Creates a Credentials instance from a signer and service account
|
| 395 |
+
info.
|
| 396 |
+
|
| 397 |
+
Args:
|
| 398 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 399 |
+
info (Mapping[str, str]): The service account info.
|
| 400 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 401 |
+
|
| 402 |
+
Returns:
|
| 403 |
+
google.auth.jwt.Credentials: The constructed credentials.
|
| 404 |
+
|
| 405 |
+
Raises:
|
| 406 |
+
ValueError: If the info is not in the expected format.
|
| 407 |
+
"""
|
| 408 |
+
kwargs.setdefault("subject", info["client_email"])
|
| 409 |
+
kwargs.setdefault("issuer", info["client_email"])
|
| 410 |
+
return cls(signer, **kwargs)
|
| 411 |
+
|
| 412 |
+
@classmethod
|
| 413 |
+
def from_service_account_info(cls, info, **kwargs):
|
| 414 |
+
"""Creates an Credentials instance from a dictionary.
|
| 415 |
+
|
| 416 |
+
Args:
|
| 417 |
+
info (Mapping[str, str]): The service account info in Google
|
| 418 |
+
format.
|
| 419 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 420 |
+
|
| 421 |
+
Returns:
|
| 422 |
+
google.auth.jwt.Credentials: The constructed credentials.
|
| 423 |
+
|
| 424 |
+
Raises:
|
| 425 |
+
ValueError: If the info is not in the expected format.
|
| 426 |
+
"""
|
| 427 |
+
signer = _service_account_info.from_dict(info, require=["client_email"])
|
| 428 |
+
return cls._from_signer_and_info(signer, info, **kwargs)
|
| 429 |
+
|
| 430 |
+
@classmethod
|
| 431 |
+
def from_service_account_file(cls, filename, **kwargs):
|
| 432 |
+
"""Creates a Credentials instance from a service account .json file
|
| 433 |
+
in Google format.
|
| 434 |
+
|
| 435 |
+
Args:
|
| 436 |
+
filename (str): The path to the service account .json file.
|
| 437 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 438 |
+
|
| 439 |
+
Returns:
|
| 440 |
+
google.auth.jwt.Credentials: The constructed credentials.
|
| 441 |
+
"""
|
| 442 |
+
info, signer = _service_account_info.from_filename(
|
| 443 |
+
filename, require=["client_email"]
|
| 444 |
+
)
|
| 445 |
+
return cls._from_signer_and_info(signer, info, **kwargs)
|
| 446 |
+
|
| 447 |
+
@classmethod
|
| 448 |
+
def from_signing_credentials(cls, credentials, audience, **kwargs):
|
| 449 |
+
"""Creates a new :class:`google.auth.jwt.Credentials` instance from an
|
| 450 |
+
existing :class:`google.auth.credentials.Signing` instance.
|
| 451 |
+
|
| 452 |
+
The new instance will use the same signer as the existing instance and
|
| 453 |
+
will use the existing instance's signer email as the issuer and
|
| 454 |
+
subject by default.
|
| 455 |
+
|
| 456 |
+
Example::
|
| 457 |
+
|
| 458 |
+
svc_creds = service_account.Credentials.from_service_account_file(
|
| 459 |
+
'service_account.json')
|
| 460 |
+
audience = (
|
| 461 |
+
'https://pubsub.googleapis.com/google.pubsub.v1.Publisher')
|
| 462 |
+
jwt_creds = jwt.Credentials.from_signing_credentials(
|
| 463 |
+
svc_creds, audience=audience)
|
| 464 |
+
|
| 465 |
+
Args:
|
| 466 |
+
credentials (google.auth.credentials.Signing): The credentials to
|
| 467 |
+
use to construct the new credentials.
|
| 468 |
+
audience (str): the `aud` claim. The intended audience for the
|
| 469 |
+
credentials.
|
| 470 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 471 |
+
|
| 472 |
+
Returns:
|
| 473 |
+
google.auth.jwt.Credentials: A new Credentials instance.
|
| 474 |
+
"""
|
| 475 |
+
kwargs.setdefault("issuer", credentials.signer_email)
|
| 476 |
+
kwargs.setdefault("subject", credentials.signer_email)
|
| 477 |
+
return cls(credentials.signer, audience=audience, **kwargs)
|
| 478 |
+
|
| 479 |
+
def with_claims(
|
| 480 |
+
self, issuer=None, subject=None, audience=None, additional_claims=None
|
| 481 |
+
):
|
| 482 |
+
"""Returns a copy of these credentials with modified claims.
|
| 483 |
+
|
| 484 |
+
Args:
|
| 485 |
+
issuer (str): The `iss` claim. If unspecified the current issuer
|
| 486 |
+
claim will be used.
|
| 487 |
+
subject (str): The `sub` claim. If unspecified the current subject
|
| 488 |
+
claim will be used.
|
| 489 |
+
audience (str): the `aud` claim. If unspecified the current
|
| 490 |
+
audience claim will be used.
|
| 491 |
+
additional_claims (Mapping[str, str]): Any additional claims for
|
| 492 |
+
the JWT payload. This will be merged with the current
|
| 493 |
+
additional claims.
|
| 494 |
+
|
| 495 |
+
Returns:
|
| 496 |
+
google.auth.jwt.Credentials: A new credentials instance.
|
| 497 |
+
"""
|
| 498 |
+
new_additional_claims = copy.deepcopy(self._additional_claims)
|
| 499 |
+
new_additional_claims.update(additional_claims or {})
|
| 500 |
+
|
| 501 |
+
return self.__class__(
|
| 502 |
+
self._signer,
|
| 503 |
+
issuer=issuer if issuer is not None else self._issuer,
|
| 504 |
+
subject=subject if subject is not None else self._subject,
|
| 505 |
+
audience=audience if audience is not None else self._audience,
|
| 506 |
+
additional_claims=new_additional_claims,
|
| 507 |
+
quota_project_id=self._quota_project_id,
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
@_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
|
| 511 |
+
def with_quota_project(self, quota_project_id):
|
| 512 |
+
return self.__class__(
|
| 513 |
+
self._signer,
|
| 514 |
+
issuer=self._issuer,
|
| 515 |
+
subject=self._subject,
|
| 516 |
+
audience=self._audience,
|
| 517 |
+
additional_claims=self._additional_claims,
|
| 518 |
+
quota_project_id=quota_project_id,
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
def _make_jwt(self):
|
| 522 |
+
"""Make a signed JWT.
|
| 523 |
+
|
| 524 |
+
Returns:
|
| 525 |
+
Tuple[bytes, datetime]: The encoded JWT and the expiration.
|
| 526 |
+
"""
|
| 527 |
+
now = _helpers.utcnow()
|
| 528 |
+
lifetime = datetime.timedelta(seconds=self._token_lifetime)
|
| 529 |
+
expiry = now + lifetime
|
| 530 |
+
|
| 531 |
+
payload = {
|
| 532 |
+
"iss": self._issuer,
|
| 533 |
+
"sub": self._subject,
|
| 534 |
+
"iat": _helpers.datetime_to_secs(now),
|
| 535 |
+
"exp": _helpers.datetime_to_secs(expiry),
|
| 536 |
+
}
|
| 537 |
+
if self._audience:
|
| 538 |
+
payload["aud"] = self._audience
|
| 539 |
+
|
| 540 |
+
payload.update(self._additional_claims)
|
| 541 |
+
|
| 542 |
+
jwt = encode(self._signer, payload)
|
| 543 |
+
|
| 544 |
+
return jwt, expiry
|
| 545 |
+
|
| 546 |
+
def refresh(self, request):
|
| 547 |
+
"""Refreshes the access token.
|
| 548 |
+
|
| 549 |
+
Args:
|
| 550 |
+
request (Any): Unused.
|
| 551 |
+
"""
|
| 552 |
+
# pylint: disable=unused-argument
|
| 553 |
+
# (pylint doesn't correctly recognize overridden methods.)
|
| 554 |
+
self.token, self.expiry = self._make_jwt()
|
| 555 |
+
|
| 556 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 557 |
+
def sign_bytes(self, message):
|
| 558 |
+
return self._signer.sign(message)
|
| 559 |
+
|
| 560 |
+
@property # type: ignore
|
| 561 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 562 |
+
def signer_email(self):
|
| 563 |
+
return self._issuer
|
| 564 |
+
|
| 565 |
+
@property # type: ignore
|
| 566 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 567 |
+
def signer(self):
|
| 568 |
+
return self._signer
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
class OnDemandCredentials(
|
| 572 |
+
google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
|
| 573 |
+
):
|
| 574 |
+
"""On-demand JWT credentials.
|
| 575 |
+
|
| 576 |
+
Like :class:`Credentials`, this class uses a JWT as the bearer token for
|
| 577 |
+
authentication. However, this class does not require the audience at
|
| 578 |
+
construction time. Instead, it will generate a new token on-demand for
|
| 579 |
+
each request using the request URI as the audience. It caches tokens
|
| 580 |
+
so that multiple requests to the same URI do not incur the overhead
|
| 581 |
+
of generating a new token every time.
|
| 582 |
+
|
| 583 |
+
This behavior is especially useful for `gRPC`_ clients. A gRPC service may
|
| 584 |
+
have multiple audience and gRPC clients may not know all of the audiences
|
| 585 |
+
required for accessing a particular service. With these credentials,
|
| 586 |
+
no knowledge of the audiences is required ahead of time.
|
| 587 |
+
|
| 588 |
+
.. _grpc: http://www.grpc.io/
|
| 589 |
+
"""
|
| 590 |
+
|
| 591 |
+
def __init__(
|
| 592 |
+
self,
|
| 593 |
+
signer,
|
| 594 |
+
issuer,
|
| 595 |
+
subject,
|
| 596 |
+
additional_claims=None,
|
| 597 |
+
token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
|
| 598 |
+
max_cache_size=_DEFAULT_MAX_CACHE_SIZE,
|
| 599 |
+
quota_project_id=None,
|
| 600 |
+
):
|
| 601 |
+
"""
|
| 602 |
+
Args:
|
| 603 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 604 |
+
issuer (str): The `iss` claim.
|
| 605 |
+
subject (str): The `sub` claim.
|
| 606 |
+
additional_claims (Mapping[str, str]): Any additional claims for
|
| 607 |
+
the JWT payload.
|
| 608 |
+
token_lifetime (int): The amount of time in seconds for
|
| 609 |
+
which the token is valid. Defaults to 1 hour.
|
| 610 |
+
max_cache_size (int): The maximum number of JWT tokens to keep in
|
| 611 |
+
cache. Tokens are cached using :class:`cachetools.LRUCache`.
|
| 612 |
+
quota_project_id (Optional[str]): The project ID used for quota
|
| 613 |
+
and billing.
|
| 614 |
+
|
| 615 |
+
"""
|
| 616 |
+
super(OnDemandCredentials, self).__init__()
|
| 617 |
+
self._signer = signer
|
| 618 |
+
self._issuer = issuer
|
| 619 |
+
self._subject = subject
|
| 620 |
+
self._token_lifetime = token_lifetime
|
| 621 |
+
self._quota_project_id = quota_project_id
|
| 622 |
+
|
| 623 |
+
if additional_claims is None:
|
| 624 |
+
additional_claims = {}
|
| 625 |
+
|
| 626 |
+
self._additional_claims = additional_claims
|
| 627 |
+
self._cache = cachetools.LRUCache(maxsize=max_cache_size)
|
| 628 |
+
|
| 629 |
+
@classmethod
|
| 630 |
+
def _from_signer_and_info(cls, signer, info, **kwargs):
|
| 631 |
+
"""Creates an OnDemandCredentials instance from a signer and service
|
| 632 |
+
account info.
|
| 633 |
+
|
| 634 |
+
Args:
|
| 635 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 636 |
+
info (Mapping[str, str]): The service account info.
|
| 637 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 638 |
+
|
| 639 |
+
Returns:
|
| 640 |
+
google.auth.jwt.OnDemandCredentials: The constructed credentials.
|
| 641 |
+
|
| 642 |
+
Raises:
|
| 643 |
+
ValueError: If the info is not in the expected format.
|
| 644 |
+
"""
|
| 645 |
+
kwargs.setdefault("subject", info["client_email"])
|
| 646 |
+
kwargs.setdefault("issuer", info["client_email"])
|
| 647 |
+
return cls(signer, **kwargs)
|
| 648 |
+
|
| 649 |
+
@classmethod
|
| 650 |
+
def from_service_account_info(cls, info, **kwargs):
|
| 651 |
+
"""Creates an OnDemandCredentials instance from a dictionary.
|
| 652 |
+
|
| 653 |
+
Args:
|
| 654 |
+
info (Mapping[str, str]): The service account info in Google
|
| 655 |
+
format.
|
| 656 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 657 |
+
|
| 658 |
+
Returns:
|
| 659 |
+
google.auth.jwt.OnDemandCredentials: The constructed credentials.
|
| 660 |
+
|
| 661 |
+
Raises:
|
| 662 |
+
ValueError: If the info is not in the expected format.
|
| 663 |
+
"""
|
| 664 |
+
signer = _service_account_info.from_dict(info, require=["client_email"])
|
| 665 |
+
return cls._from_signer_and_info(signer, info, **kwargs)
|
| 666 |
+
|
| 667 |
+
@classmethod
|
| 668 |
+
def from_service_account_file(cls, filename, **kwargs):
|
| 669 |
+
"""Creates an OnDemandCredentials instance from a service account .json
|
| 670 |
+
file in Google format.
|
| 671 |
+
|
| 672 |
+
Args:
|
| 673 |
+
filename (str): The path to the service account .json file.
|
| 674 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 675 |
+
|
| 676 |
+
Returns:
|
| 677 |
+
google.auth.jwt.OnDemandCredentials: The constructed credentials.
|
| 678 |
+
"""
|
| 679 |
+
info, signer = _service_account_info.from_filename(
|
| 680 |
+
filename, require=["client_email"]
|
| 681 |
+
)
|
| 682 |
+
return cls._from_signer_and_info(signer, info, **kwargs)
|
| 683 |
+
|
| 684 |
+
@classmethod
|
| 685 |
+
def from_signing_credentials(cls, credentials, **kwargs):
|
| 686 |
+
"""Creates a new :class:`google.auth.jwt.OnDemandCredentials` instance
|
| 687 |
+
from an existing :class:`google.auth.credentials.Signing` instance.
|
| 688 |
+
|
| 689 |
+
The new instance will use the same signer as the existing instance and
|
| 690 |
+
will use the existing instance's signer email as the issuer and
|
| 691 |
+
subject by default.
|
| 692 |
+
|
| 693 |
+
Example::
|
| 694 |
+
|
| 695 |
+
svc_creds = service_account.Credentials.from_service_account_file(
|
| 696 |
+
'service_account.json')
|
| 697 |
+
jwt_creds = jwt.OnDemandCredentials.from_signing_credentials(
|
| 698 |
+
svc_creds)
|
| 699 |
+
|
| 700 |
+
Args:
|
| 701 |
+
credentials (google.auth.credentials.Signing): The credentials to
|
| 702 |
+
use to construct the new credentials.
|
| 703 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 704 |
+
|
| 705 |
+
Returns:
|
| 706 |
+
google.auth.jwt.Credentials: A new Credentials instance.
|
| 707 |
+
"""
|
| 708 |
+
kwargs.setdefault("issuer", credentials.signer_email)
|
| 709 |
+
kwargs.setdefault("subject", credentials.signer_email)
|
| 710 |
+
return cls(credentials.signer, **kwargs)
|
| 711 |
+
|
| 712 |
+
def with_claims(self, issuer=None, subject=None, additional_claims=None):
|
| 713 |
+
"""Returns a copy of these credentials with modified claims.
|
| 714 |
+
|
| 715 |
+
Args:
|
| 716 |
+
issuer (str): The `iss` claim. If unspecified the current issuer
|
| 717 |
+
claim will be used.
|
| 718 |
+
subject (str): The `sub` claim. If unspecified the current subject
|
| 719 |
+
claim will be used.
|
| 720 |
+
additional_claims (Mapping[str, str]): Any additional claims for
|
| 721 |
+
the JWT payload. This will be merged with the current
|
| 722 |
+
additional claims.
|
| 723 |
+
|
| 724 |
+
Returns:
|
| 725 |
+
google.auth.jwt.OnDemandCredentials: A new credentials instance.
|
| 726 |
+
"""
|
| 727 |
+
new_additional_claims = copy.deepcopy(self._additional_claims)
|
| 728 |
+
new_additional_claims.update(additional_claims or {})
|
| 729 |
+
|
| 730 |
+
return self.__class__(
|
| 731 |
+
self._signer,
|
| 732 |
+
issuer=issuer if issuer is not None else self._issuer,
|
| 733 |
+
subject=subject if subject is not None else self._subject,
|
| 734 |
+
additional_claims=new_additional_claims,
|
| 735 |
+
max_cache_size=self._cache.maxsize,
|
| 736 |
+
quota_project_id=self._quota_project_id,
|
| 737 |
+
)
|
| 738 |
+
|
| 739 |
+
@_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
|
| 740 |
+
def with_quota_project(self, quota_project_id):
|
| 741 |
+
|
| 742 |
+
return self.__class__(
|
| 743 |
+
self._signer,
|
| 744 |
+
issuer=self._issuer,
|
| 745 |
+
subject=self._subject,
|
| 746 |
+
additional_claims=self._additional_claims,
|
| 747 |
+
max_cache_size=self._cache.maxsize,
|
| 748 |
+
quota_project_id=quota_project_id,
|
| 749 |
+
)
|
| 750 |
+
|
| 751 |
+
@property
|
| 752 |
+
def valid(self):
|
| 753 |
+
"""Checks the validity of the credentials.
|
| 754 |
+
|
| 755 |
+
These credentials are always valid because it generates tokens on
|
| 756 |
+
demand.
|
| 757 |
+
"""
|
| 758 |
+
return True
|
| 759 |
+
|
| 760 |
+
def _make_jwt_for_audience(self, audience):
|
| 761 |
+
"""Make a new JWT for the given audience.
|
| 762 |
+
|
| 763 |
+
Args:
|
| 764 |
+
audience (str): The intended audience.
|
| 765 |
+
|
| 766 |
+
Returns:
|
| 767 |
+
Tuple[bytes, datetime]: The encoded JWT and the expiration.
|
| 768 |
+
"""
|
| 769 |
+
now = _helpers.utcnow()
|
| 770 |
+
lifetime = datetime.timedelta(seconds=self._token_lifetime)
|
| 771 |
+
expiry = now + lifetime
|
| 772 |
+
|
| 773 |
+
payload = {
|
| 774 |
+
"iss": self._issuer,
|
| 775 |
+
"sub": self._subject,
|
| 776 |
+
"iat": _helpers.datetime_to_secs(now),
|
| 777 |
+
"exp": _helpers.datetime_to_secs(expiry),
|
| 778 |
+
"aud": audience,
|
| 779 |
+
}
|
| 780 |
+
|
| 781 |
+
payload.update(self._additional_claims)
|
| 782 |
+
|
| 783 |
+
jwt = encode(self._signer, payload)
|
| 784 |
+
|
| 785 |
+
return jwt, expiry
|
| 786 |
+
|
| 787 |
+
def _get_jwt_for_audience(self, audience):
|
| 788 |
+
"""Get a JWT For a given audience.
|
| 789 |
+
|
| 790 |
+
If there is already an existing, non-expired token in the cache for
|
| 791 |
+
the audience, that token is used. Otherwise, a new token will be
|
| 792 |
+
created.
|
| 793 |
+
|
| 794 |
+
Args:
|
| 795 |
+
audience (str): The intended audience.
|
| 796 |
+
|
| 797 |
+
Returns:
|
| 798 |
+
bytes: The encoded JWT.
|
| 799 |
+
"""
|
| 800 |
+
token, expiry = self._cache.get(audience, (None, None))
|
| 801 |
+
|
| 802 |
+
if token is None or expiry < _helpers.utcnow():
|
| 803 |
+
token, expiry = self._make_jwt_for_audience(audience)
|
| 804 |
+
self._cache[audience] = token, expiry
|
| 805 |
+
|
| 806 |
+
return token
|
| 807 |
+
|
| 808 |
+
def refresh(self, request):
|
| 809 |
+
"""Raises an exception, these credentials can not be directly
|
| 810 |
+
refreshed.
|
| 811 |
+
|
| 812 |
+
Args:
|
| 813 |
+
request (Any): Unused.
|
| 814 |
+
|
| 815 |
+
Raises:
|
| 816 |
+
google.auth.RefreshError
|
| 817 |
+
"""
|
| 818 |
+
# pylint: disable=unused-argument
|
| 819 |
+
# (pylint doesn't correctly recognize overridden methods.)
|
| 820 |
+
raise exceptions.RefreshError(
|
| 821 |
+
"OnDemandCredentials can not be directly refreshed."
|
| 822 |
+
)
|
| 823 |
+
|
| 824 |
+
def before_request(self, request, method, url, headers):
|
| 825 |
+
"""Performs credential-specific before request logic.
|
| 826 |
+
|
| 827 |
+
Args:
|
| 828 |
+
request (Any): Unused. JWT credentials do not need to make an
|
| 829 |
+
HTTP request to refresh.
|
| 830 |
+
method (str): The request's HTTP method.
|
| 831 |
+
url (str): The request's URI. This is used as the audience claim
|
| 832 |
+
when generating the JWT.
|
| 833 |
+
headers (Mapping): The request's headers.
|
| 834 |
+
"""
|
| 835 |
+
# pylint: disable=unused-argument
|
| 836 |
+
# (pylint doesn't correctly recognize overridden methods.)
|
| 837 |
+
parts = urllib.parse.urlsplit(url)
|
| 838 |
+
# Strip query string and fragment
|
| 839 |
+
audience = urllib.parse.urlunsplit(
|
| 840 |
+
(parts.scheme, parts.netloc, parts.path, "", "")
|
| 841 |
+
)
|
| 842 |
+
token = self._get_jwt_for_audience(audience)
|
| 843 |
+
self.apply(headers, token=token)
|
| 844 |
+
|
| 845 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 846 |
+
def sign_bytes(self, message):
|
| 847 |
+
return self._signer.sign(message)
|
| 848 |
+
|
| 849 |
+
@property # type: ignore
|
| 850 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 851 |
+
def signer_email(self):
|
| 852 |
+
return self._issuer
|
| 853 |
+
|
| 854 |
+
@property # type: ignore
|
| 855 |
+
@_helpers.copy_docstring(google.auth.credentials.Signing)
|
| 856 |
+
def signer(self):
|
| 857 |
+
return self._signer
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/auth/version.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
__version__ = "2.9.0"
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (213 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_client.cpython-38.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_client_async.cpython-38.pyc
ADDED
|
Binary file (7.91 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_credentials_async.cpython-38.pyc
ADDED
|
Binary file (3.33 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_id_token_async.cpython-38.pyc
ADDED
|
Binary file (8.59 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_reauth_async.cpython-38.pyc
ADDED
|
Binary file (9.05 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/_service_account_async.cpython-38.pyc
ADDED
|
Binary file (4.74 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/challenges.cpython-38.pyc
ADDED
|
Binary file (6.12 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/credentials.cpython-38.pyc
ADDED
|
Binary file (16.3 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/gdch_credentials.cpython-38.pyc
ADDED
|
Binary file (7.93 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/id_token.cpython-38.pyc
ADDED
|
Binary file (10.3 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/reauth.cpython-38.pyc
ADDED
|
Binary file (9.49 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/service_account.cpython-38.pyc
ADDED
|
Binary file (21.4 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/sts.cpython-38.pyc
ADDED
|
Binary file (4.65 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/__pycache__/utils.cpython-38.pyc
ADDED
|
Binary file (5.52 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/_id_token_async.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Google ID Token helpers.
|
| 16 |
+
|
| 17 |
+
Provides support for verifying `OpenID Connect ID Tokens`_, especially ones
|
| 18 |
+
generated by Google infrastructure.
|
| 19 |
+
|
| 20 |
+
To parse and verify an ID Token issued by Google's OAuth 2.0 authorization
|
| 21 |
+
server use :func:`verify_oauth2_token`. To verify an ID Token issued by
|
| 22 |
+
Firebase, use :func:`verify_firebase_token`.
|
| 23 |
+
|
| 24 |
+
A general purpose ID Token verifier is available as :func:`verify_token`.
|
| 25 |
+
|
| 26 |
+
Example::
|
| 27 |
+
|
| 28 |
+
from google.oauth2 import _id_token_async
|
| 29 |
+
from google.auth.transport import aiohttp_requests
|
| 30 |
+
|
| 31 |
+
request = aiohttp_requests.Request()
|
| 32 |
+
|
| 33 |
+
id_info = await _id_token_async.verify_oauth2_token(
|
| 34 |
+
token, request, 'my-client-id.example.com')
|
| 35 |
+
|
| 36 |
+
if id_info['iss'] != 'https://accounts.google.com':
|
| 37 |
+
raise ValueError('Wrong issuer.')
|
| 38 |
+
|
| 39 |
+
userid = id_info['sub']
|
| 40 |
+
|
| 41 |
+
By default, this will re-fetch certificates for each verification. Because
|
| 42 |
+
Google's public keys are only changed infrequently (on the order of once per
|
| 43 |
+
day), you may wish to take advantage of caching to reduce latency and the
|
| 44 |
+
potential for network errors. This can be accomplished using an external
|
| 45 |
+
library like `CacheControl`_ to create a cache-aware
|
| 46 |
+
:class:`google.auth.transport.Request`::
|
| 47 |
+
|
| 48 |
+
import cachecontrol
|
| 49 |
+
import google.auth.transport.requests
|
| 50 |
+
import requests
|
| 51 |
+
|
| 52 |
+
session = requests.session()
|
| 53 |
+
cached_session = cachecontrol.CacheControl(session)
|
| 54 |
+
request = google.auth.transport.requests.Request(session=cached_session)
|
| 55 |
+
|
| 56 |
+
.. _OpenID Connect ID Token:
|
| 57 |
+
http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
| 58 |
+
.. _CacheControl: https://cachecontrol.readthedocs.io
|
| 59 |
+
"""
|
| 60 |
+
|
| 61 |
+
import json
|
| 62 |
+
import os
|
| 63 |
+
|
| 64 |
+
import six
|
| 65 |
+
from six.moves import http_client
|
| 66 |
+
|
| 67 |
+
from google.auth import environment_vars
|
| 68 |
+
from google.auth import exceptions
|
| 69 |
+
from google.auth import jwt
|
| 70 |
+
from google.auth.transport import requests
|
| 71 |
+
from google.oauth2 import id_token as sync_id_token
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
async def _fetch_certs(request, certs_url):
|
| 75 |
+
"""Fetches certificates.
|
| 76 |
+
|
| 77 |
+
Google-style cerificate endpoints return JSON in the format of
|
| 78 |
+
``{'key id': 'x509 certificate'}``.
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
request (google.auth.transport.Request): The object used to make
|
| 82 |
+
HTTP requests. This must be an aiohttp request.
|
| 83 |
+
certs_url (str): The certificate endpoint URL.
|
| 84 |
+
|
| 85 |
+
Returns:
|
| 86 |
+
Mapping[str, str]: A mapping of public key ID to x.509 certificate
|
| 87 |
+
data.
|
| 88 |
+
"""
|
| 89 |
+
response = await request(certs_url, method="GET")
|
| 90 |
+
|
| 91 |
+
if response.status != http_client.OK:
|
| 92 |
+
raise exceptions.TransportError(
|
| 93 |
+
"Could not fetch certificates at {}".format(certs_url)
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
data = await response.data.read()
|
| 97 |
+
|
| 98 |
+
return json.loads(json.dumps(data))
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
async def verify_token(
|
| 102 |
+
id_token,
|
| 103 |
+
request,
|
| 104 |
+
audience=None,
|
| 105 |
+
certs_url=sync_id_token._GOOGLE_OAUTH2_CERTS_URL,
|
| 106 |
+
clock_skew_in_seconds=0,
|
| 107 |
+
):
|
| 108 |
+
"""Verifies an ID token and returns the decoded token.
|
| 109 |
+
|
| 110 |
+
Args:
|
| 111 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 112 |
+
request (google.auth.transport.Request): The object used to make
|
| 113 |
+
HTTP requests. This must be an aiohttp request.
|
| 114 |
+
audience (str): The audience that this token is intended for. If None
|
| 115 |
+
then the audience is not verified.
|
| 116 |
+
certs_url (str): The URL that specifies the certificates to use to
|
| 117 |
+
verify the token. This URL should return JSON in the format of
|
| 118 |
+
``{'key id': 'x509 certificate'}``.
|
| 119 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 120 |
+
validation.
|
| 121 |
+
|
| 122 |
+
Returns:
|
| 123 |
+
Mapping[str, Any]: The decoded token.
|
| 124 |
+
"""
|
| 125 |
+
certs = await _fetch_certs(request, certs_url)
|
| 126 |
+
|
| 127 |
+
return jwt.decode(
|
| 128 |
+
id_token,
|
| 129 |
+
certs=certs,
|
| 130 |
+
audience=audience,
|
| 131 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
async def verify_oauth2_token(
|
| 136 |
+
id_token, request, audience=None, clock_skew_in_seconds=0
|
| 137 |
+
):
|
| 138 |
+
"""Verifies an ID Token issued by Google's OAuth 2.0 authorization server.
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 142 |
+
request (google.auth.transport.Request): The object used to make
|
| 143 |
+
HTTP requests. This must be an aiohttp request.
|
| 144 |
+
audience (str): The audience that this token is intended for. This is
|
| 145 |
+
typically your application's OAuth 2.0 client ID. If None then the
|
| 146 |
+
audience is not verified.
|
| 147 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 148 |
+
validation.
|
| 149 |
+
|
| 150 |
+
Returns:
|
| 151 |
+
Mapping[str, Any]: The decoded token.
|
| 152 |
+
|
| 153 |
+
Raises:
|
| 154 |
+
exceptions.GoogleAuthError: If the issuer is invalid.
|
| 155 |
+
"""
|
| 156 |
+
idinfo = await verify_token(
|
| 157 |
+
id_token,
|
| 158 |
+
request,
|
| 159 |
+
audience=audience,
|
| 160 |
+
certs_url=sync_id_token._GOOGLE_OAUTH2_CERTS_URL,
|
| 161 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
if idinfo["iss"] not in sync_id_token._GOOGLE_ISSUERS:
|
| 165 |
+
raise exceptions.GoogleAuthError(
|
| 166 |
+
"Wrong issuer. 'iss' should be one of the following: {}".format(
|
| 167 |
+
sync_id_token._GOOGLE_ISSUERS
|
| 168 |
+
)
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
return idinfo
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
async def verify_firebase_token(
|
| 175 |
+
id_token, request, audience=None, clock_skew_in_seconds=0
|
| 176 |
+
):
|
| 177 |
+
"""Verifies an ID Token issued by Firebase Authentication.
|
| 178 |
+
|
| 179 |
+
Args:
|
| 180 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 181 |
+
request (google.auth.transport.Request): The object used to make
|
| 182 |
+
HTTP requests. This must be an aiohttp request.
|
| 183 |
+
audience (str): The audience that this token is intended for. This is
|
| 184 |
+
typically your Firebase application ID. If None then the audience
|
| 185 |
+
is not verified.
|
| 186 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 187 |
+
validation.
|
| 188 |
+
|
| 189 |
+
Returns:
|
| 190 |
+
Mapping[str, Any]: The decoded token.
|
| 191 |
+
"""
|
| 192 |
+
return await verify_token(
|
| 193 |
+
id_token,
|
| 194 |
+
request,
|
| 195 |
+
audience=audience,
|
| 196 |
+
certs_url=sync_id_token._GOOGLE_APIS_CERTS_URL,
|
| 197 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
async def fetch_id_token(request, audience):
|
| 202 |
+
"""Fetch the ID Token from the current environment.
|
| 203 |
+
|
| 204 |
+
This function acquires ID token from the environment in the following order.
|
| 205 |
+
See https://google.aip.dev/auth/4110.
|
| 206 |
+
|
| 207 |
+
1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
|
| 208 |
+
to the path of a valid service account JSON file, then ID token is
|
| 209 |
+
acquired using this service account credentials.
|
| 210 |
+
2. If the application is running in Compute Engine, App Engine or Cloud Run,
|
| 211 |
+
then the ID token are obtained from the metadata server.
|
| 212 |
+
3. If metadata server doesn't exist and no valid service account credentials
|
| 213 |
+
are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
|
| 214 |
+
be raised.
|
| 215 |
+
|
| 216 |
+
Example::
|
| 217 |
+
|
| 218 |
+
import google.oauth2._id_token_async
|
| 219 |
+
import google.auth.transport.aiohttp_requests
|
| 220 |
+
|
| 221 |
+
request = google.auth.transport.aiohttp_requests.Request()
|
| 222 |
+
target_audience = "https://pubsub.googleapis.com"
|
| 223 |
+
|
| 224 |
+
id_token = await google.oauth2._id_token_async.fetch_id_token(request, target_audience)
|
| 225 |
+
|
| 226 |
+
Args:
|
| 227 |
+
request (google.auth.transport.aiohttp_requests.Request): A callable used to make
|
| 228 |
+
HTTP requests.
|
| 229 |
+
audience (str): The audience that this ID token is intended for.
|
| 230 |
+
|
| 231 |
+
Returns:
|
| 232 |
+
str: The ID token.
|
| 233 |
+
|
| 234 |
+
Raises:
|
| 235 |
+
~google.auth.exceptions.DefaultCredentialsError:
|
| 236 |
+
If metadata server doesn't exist and no valid service account
|
| 237 |
+
credentials are found.
|
| 238 |
+
"""
|
| 239 |
+
# 1. Try to get credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
|
| 240 |
+
# variable.
|
| 241 |
+
credentials_filename = os.environ.get(environment_vars.CREDENTIALS)
|
| 242 |
+
if credentials_filename:
|
| 243 |
+
if not (
|
| 244 |
+
os.path.exists(credentials_filename)
|
| 245 |
+
and os.path.isfile(credentials_filename)
|
| 246 |
+
):
|
| 247 |
+
raise exceptions.DefaultCredentialsError(
|
| 248 |
+
"GOOGLE_APPLICATION_CREDENTIALS path is either not found or invalid."
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
try:
|
| 252 |
+
with open(credentials_filename, "r") as f:
|
| 253 |
+
from google.oauth2 import _service_account_async as service_account
|
| 254 |
+
|
| 255 |
+
info = json.load(f)
|
| 256 |
+
if info.get("type") == "service_account":
|
| 257 |
+
credentials = service_account.IDTokenCredentials.from_service_account_info(
|
| 258 |
+
info, target_audience=audience
|
| 259 |
+
)
|
| 260 |
+
await credentials.refresh(request)
|
| 261 |
+
return credentials.token
|
| 262 |
+
except ValueError as caught_exc:
|
| 263 |
+
new_exc = exceptions.DefaultCredentialsError(
|
| 264 |
+
"GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
|
| 265 |
+
caught_exc,
|
| 266 |
+
)
|
| 267 |
+
six.raise_from(new_exc, caught_exc)
|
| 268 |
+
|
| 269 |
+
# 2. Try to fetch ID token from metada server if it exists. The code works
|
| 270 |
+
# for GAE and Cloud Run metadata server as well.
|
| 271 |
+
try:
|
| 272 |
+
from google.auth import compute_engine
|
| 273 |
+
from google.auth.compute_engine import _metadata
|
| 274 |
+
|
| 275 |
+
request_new = requests.Request()
|
| 276 |
+
if _metadata.ping(request_new):
|
| 277 |
+
credentials = compute_engine.IDTokenCredentials(
|
| 278 |
+
request_new, audience, use_metadata_identity_endpoint=True
|
| 279 |
+
)
|
| 280 |
+
credentials.refresh(request_new)
|
| 281 |
+
return credentials.token
|
| 282 |
+
except (ImportError, exceptions.TransportError):
|
| 283 |
+
pass
|
| 284 |
+
|
| 285 |
+
raise exceptions.DefaultCredentialsError(
|
| 286 |
+
"Neither metadata server or valid service account credentials are found."
|
| 287 |
+
)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/_reauth_async.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""A module that provides functions for handling rapt authentication.
|
| 16 |
+
|
| 17 |
+
Reauth is a process of obtaining additional authentication (such as password,
|
| 18 |
+
security token, etc.) while refreshing OAuth 2.0 credentials for a user.
|
| 19 |
+
|
| 20 |
+
Credentials that use the Reauth flow must have the reauth scope,
|
| 21 |
+
``https://www.googleapis.com/auth/accounts.reauth``.
|
| 22 |
+
|
| 23 |
+
This module provides a high-level function for executing the Reauth process,
|
| 24 |
+
:func:`refresh_grant`, and lower-level helpers for doing the individual
|
| 25 |
+
steps of the reauth process.
|
| 26 |
+
|
| 27 |
+
Those steps are:
|
| 28 |
+
|
| 29 |
+
1. Obtaining a list of challenges from the reauth server.
|
| 30 |
+
2. Running through each challenge and sending the result back to the reauth
|
| 31 |
+
server.
|
| 32 |
+
3. Refreshing the access token using the returned rapt token.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
import sys
|
| 36 |
+
|
| 37 |
+
from six.moves import range
|
| 38 |
+
|
| 39 |
+
from google.auth import exceptions
|
| 40 |
+
from google.oauth2 import _client
|
| 41 |
+
from google.oauth2 import _client_async
|
| 42 |
+
from google.oauth2 import challenges
|
| 43 |
+
from google.oauth2 import reauth
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
async def _get_challenges(
|
| 47 |
+
request, supported_challenge_types, access_token, requested_scopes=None
|
| 48 |
+
):
|
| 49 |
+
"""Does initial request to reauth API to get the challenges.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
request (google.auth.transport.Request): A callable used to make
|
| 53 |
+
HTTP requests. This must be an aiohttp request.
|
| 54 |
+
supported_challenge_types (Sequence[str]): list of challenge names
|
| 55 |
+
supported by the manager.
|
| 56 |
+
access_token (str): Access token with reauth scopes.
|
| 57 |
+
requested_scopes (Optional(Sequence[str])): Authorized scopes for the credentials.
|
| 58 |
+
|
| 59 |
+
Returns:
|
| 60 |
+
dict: The response from the reauth API.
|
| 61 |
+
"""
|
| 62 |
+
body = {"supportedChallengeTypes": supported_challenge_types}
|
| 63 |
+
if requested_scopes:
|
| 64 |
+
body["oauthScopesForDomainPolicyLookup"] = requested_scopes
|
| 65 |
+
|
| 66 |
+
return await _client_async._token_endpoint_request(
|
| 67 |
+
request,
|
| 68 |
+
reauth._REAUTH_API + ":start",
|
| 69 |
+
body,
|
| 70 |
+
access_token=access_token,
|
| 71 |
+
use_json=True,
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
async def _send_challenge_result(
|
| 76 |
+
request, session_id, challenge_id, client_input, access_token
|
| 77 |
+
):
|
| 78 |
+
"""Attempt to refresh access token by sending next challenge result.
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
request (google.auth.transport.Request): A callable used to make
|
| 82 |
+
HTTP requests. This must be an aiohttp request.
|
| 83 |
+
session_id (str): session id returned by the initial reauth call.
|
| 84 |
+
challenge_id (str): challenge id returned by the initial reauth call.
|
| 85 |
+
client_input: dict with a challenge-specific client input. For example:
|
| 86 |
+
``{'credential': password}`` for password challenge.
|
| 87 |
+
access_token (str): Access token with reauth scopes.
|
| 88 |
+
|
| 89 |
+
Returns:
|
| 90 |
+
dict: The response from the reauth API.
|
| 91 |
+
"""
|
| 92 |
+
body = {
|
| 93 |
+
"sessionId": session_id,
|
| 94 |
+
"challengeId": challenge_id,
|
| 95 |
+
"action": "RESPOND",
|
| 96 |
+
"proposalResponse": client_input,
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
return await _client_async._token_endpoint_request(
|
| 100 |
+
request,
|
| 101 |
+
reauth._REAUTH_API + "/{}:continue".format(session_id),
|
| 102 |
+
body,
|
| 103 |
+
access_token=access_token,
|
| 104 |
+
use_json=True,
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
async def _run_next_challenge(msg, request, access_token):
|
| 109 |
+
"""Get the next challenge from msg and run it.
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
msg (dict): Reauth API response body (either from the initial request to
|
| 113 |
+
https://reauth.googleapis.com/v2/sessions:start or from sending the
|
| 114 |
+
previous challenge response to
|
| 115 |
+
https://reauth.googleapis.com/v2/sessions/id:continue)
|
| 116 |
+
request (google.auth.transport.Request): A callable used to make
|
| 117 |
+
HTTP requests. This must be an aiohttp request.
|
| 118 |
+
access_token (str): reauth access token
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
dict: The response from the reauth API.
|
| 122 |
+
|
| 123 |
+
Raises:
|
| 124 |
+
google.auth.exceptions.ReauthError: if reauth failed.
|
| 125 |
+
"""
|
| 126 |
+
for challenge in msg["challenges"]:
|
| 127 |
+
if challenge["status"] != "READY":
|
| 128 |
+
# Skip non-activated challenges.
|
| 129 |
+
continue
|
| 130 |
+
c = challenges.AVAILABLE_CHALLENGES.get(challenge["challengeType"], None)
|
| 131 |
+
if not c:
|
| 132 |
+
raise exceptions.ReauthFailError(
|
| 133 |
+
"Unsupported challenge type {0}. Supported types: {1}".format(
|
| 134 |
+
challenge["challengeType"],
|
| 135 |
+
",".join(list(challenges.AVAILABLE_CHALLENGES.keys())),
|
| 136 |
+
)
|
| 137 |
+
)
|
| 138 |
+
if not c.is_locally_eligible:
|
| 139 |
+
raise exceptions.ReauthFailError(
|
| 140 |
+
"Challenge {0} is not locally eligible".format(
|
| 141 |
+
challenge["challengeType"]
|
| 142 |
+
)
|
| 143 |
+
)
|
| 144 |
+
client_input = c.obtain_challenge_input(challenge)
|
| 145 |
+
if not client_input:
|
| 146 |
+
return None
|
| 147 |
+
return await _send_challenge_result(
|
| 148 |
+
request,
|
| 149 |
+
msg["sessionId"],
|
| 150 |
+
challenge["challengeId"],
|
| 151 |
+
client_input,
|
| 152 |
+
access_token,
|
| 153 |
+
)
|
| 154 |
+
return None
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
async def _obtain_rapt(request, access_token, requested_scopes):
|
| 158 |
+
"""Given an http request method and reauth access token, get rapt token.
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
request (google.auth.transport.Request): A callable used to make
|
| 162 |
+
HTTP requests. This must be an aiohttp request.
|
| 163 |
+
access_token (str): reauth access token
|
| 164 |
+
requested_scopes (Sequence[str]): scopes required by the client application
|
| 165 |
+
|
| 166 |
+
Returns:
|
| 167 |
+
str: The rapt token.
|
| 168 |
+
|
| 169 |
+
Raises:
|
| 170 |
+
google.auth.exceptions.ReauthError: if reauth failed
|
| 171 |
+
"""
|
| 172 |
+
msg = await _get_challenges(
|
| 173 |
+
request,
|
| 174 |
+
list(challenges.AVAILABLE_CHALLENGES.keys()),
|
| 175 |
+
access_token,
|
| 176 |
+
requested_scopes,
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
if msg["status"] == reauth._AUTHENTICATED:
|
| 180 |
+
return msg["encodedProofOfReauthToken"]
|
| 181 |
+
|
| 182 |
+
for _ in range(0, reauth.RUN_CHALLENGE_RETRY_LIMIT):
|
| 183 |
+
if not (
|
| 184 |
+
msg["status"] == reauth._CHALLENGE_REQUIRED
|
| 185 |
+
or msg["status"] == reauth._CHALLENGE_PENDING
|
| 186 |
+
):
|
| 187 |
+
raise exceptions.ReauthFailError(
|
| 188 |
+
"Reauthentication challenge failed due to API error: {}".format(
|
| 189 |
+
msg["status"]
|
| 190 |
+
)
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
if not reauth.is_interactive():
|
| 194 |
+
raise exceptions.ReauthFailError(
|
| 195 |
+
"Reauthentication challenge could not be answered because you are not"
|
| 196 |
+
" in an interactive session."
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
msg = await _run_next_challenge(msg, request, access_token)
|
| 200 |
+
|
| 201 |
+
if msg["status"] == reauth._AUTHENTICATED:
|
| 202 |
+
return msg["encodedProofOfReauthToken"]
|
| 203 |
+
|
| 204 |
+
# If we got here it means we didn't get authenticated.
|
| 205 |
+
raise exceptions.ReauthFailError("Failed to obtain rapt token.")
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
async def get_rapt_token(
|
| 209 |
+
request, client_id, client_secret, refresh_token, token_uri, scopes=None
|
| 210 |
+
):
|
| 211 |
+
"""Given an http request method and refresh_token, get rapt token.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
request (google.auth.transport.Request): A callable used to make
|
| 215 |
+
HTTP requests. This must be an aiohttp request.
|
| 216 |
+
client_id (str): client id to get access token for reauth scope.
|
| 217 |
+
client_secret (str): client secret for the client_id
|
| 218 |
+
refresh_token (str): refresh token to refresh access token
|
| 219 |
+
token_uri (str): uri to refresh access token
|
| 220 |
+
scopes (Optional(Sequence[str])): scopes required by the client application
|
| 221 |
+
|
| 222 |
+
Returns:
|
| 223 |
+
str: The rapt token.
|
| 224 |
+
Raises:
|
| 225 |
+
google.auth.exceptions.RefreshError: If reauth failed.
|
| 226 |
+
"""
|
| 227 |
+
sys.stderr.write("Reauthentication required.\n")
|
| 228 |
+
|
| 229 |
+
# Get access token for reauth.
|
| 230 |
+
access_token, _, _, _ = await _client_async.refresh_grant(
|
| 231 |
+
request=request,
|
| 232 |
+
client_id=client_id,
|
| 233 |
+
client_secret=client_secret,
|
| 234 |
+
refresh_token=refresh_token,
|
| 235 |
+
token_uri=token_uri,
|
| 236 |
+
scopes=[reauth._REAUTH_SCOPE],
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
# Get rapt token from reauth API.
|
| 240 |
+
rapt_token = await _obtain_rapt(request, access_token, requested_scopes=scopes)
|
| 241 |
+
|
| 242 |
+
return rapt_token
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
async def refresh_grant(
|
| 246 |
+
request,
|
| 247 |
+
token_uri,
|
| 248 |
+
refresh_token,
|
| 249 |
+
client_id,
|
| 250 |
+
client_secret,
|
| 251 |
+
scopes=None,
|
| 252 |
+
rapt_token=None,
|
| 253 |
+
enable_reauth_refresh=False,
|
| 254 |
+
):
|
| 255 |
+
"""Implements the reauthentication flow.
|
| 256 |
+
|
| 257 |
+
Args:
|
| 258 |
+
request (google.auth.transport.Request): A callable used to make
|
| 259 |
+
HTTP requests. This must be an aiohttp request.
|
| 260 |
+
token_uri (str): The OAuth 2.0 authorizations server's token endpoint
|
| 261 |
+
URI.
|
| 262 |
+
refresh_token (str): The refresh token to use to get a new access
|
| 263 |
+
token.
|
| 264 |
+
client_id (str): The OAuth 2.0 application's client ID.
|
| 265 |
+
client_secret (str): The Oauth 2.0 appliaction's client secret.
|
| 266 |
+
scopes (Optional(Sequence[str])): Scopes to request. If present, all
|
| 267 |
+
scopes must be authorized for the refresh token. Useful if refresh
|
| 268 |
+
token has a wild card scope (e.g.
|
| 269 |
+
'https://www.googleapis.com/auth/any-api').
|
| 270 |
+
rapt_token (Optional(str)): The rapt token for reauth.
|
| 271 |
+
enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow
|
| 272 |
+
should be used. The default value is False. This option is for
|
| 273 |
+
gcloud only, other users should use the default value.
|
| 274 |
+
|
| 275 |
+
Returns:
|
| 276 |
+
Tuple[str, Optional[str], Optional[datetime], Mapping[str, str], str]: The
|
| 277 |
+
access token, new refresh token, expiration, the additional data
|
| 278 |
+
returned by the token endpoint, and the rapt token.
|
| 279 |
+
|
| 280 |
+
Raises:
|
| 281 |
+
google.auth.exceptions.RefreshError: If the token endpoint returned
|
| 282 |
+
an error.
|
| 283 |
+
"""
|
| 284 |
+
body = {
|
| 285 |
+
"grant_type": _client._REFRESH_GRANT_TYPE,
|
| 286 |
+
"client_id": client_id,
|
| 287 |
+
"client_secret": client_secret,
|
| 288 |
+
"refresh_token": refresh_token,
|
| 289 |
+
}
|
| 290 |
+
if scopes:
|
| 291 |
+
body["scope"] = " ".join(scopes)
|
| 292 |
+
if rapt_token:
|
| 293 |
+
body["rapt"] = rapt_token
|
| 294 |
+
|
| 295 |
+
response_status_ok, response_data = await _client_async._token_endpoint_request_no_throw(
|
| 296 |
+
request, token_uri, body
|
| 297 |
+
)
|
| 298 |
+
if (
|
| 299 |
+
not response_status_ok
|
| 300 |
+
and response_data.get("error") == reauth._REAUTH_NEEDED_ERROR
|
| 301 |
+
and (
|
| 302 |
+
response_data.get("error_subtype")
|
| 303 |
+
== reauth._REAUTH_NEEDED_ERROR_INVALID_RAPT
|
| 304 |
+
or response_data.get("error_subtype")
|
| 305 |
+
== reauth._REAUTH_NEEDED_ERROR_RAPT_REQUIRED
|
| 306 |
+
)
|
| 307 |
+
):
|
| 308 |
+
if not enable_reauth_refresh:
|
| 309 |
+
raise exceptions.RefreshError(
|
| 310 |
+
"Reauthentication is needed. Please run `gcloud auth login --update-adc` to reauthenticate."
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
rapt_token = await get_rapt_token(
|
| 314 |
+
request, client_id, client_secret, refresh_token, token_uri, scopes=scopes
|
| 315 |
+
)
|
| 316 |
+
body["rapt"] = rapt_token
|
| 317 |
+
(
|
| 318 |
+
response_status_ok,
|
| 319 |
+
response_data,
|
| 320 |
+
) = await _client_async._token_endpoint_request_no_throw(
|
| 321 |
+
request, token_uri, body
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
if not response_status_ok:
|
| 325 |
+
_client._handle_error_response(response_data)
|
| 326 |
+
refresh_response = _client._handle_refresh_grant_response(
|
| 327 |
+
response_data, refresh_token
|
| 328 |
+
)
|
| 329 |
+
return refresh_response + (rapt_token,)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/credentials.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""OAuth 2.0 Credentials.
|
| 16 |
+
|
| 17 |
+
This module provides credentials based on OAuth 2.0 access and refresh tokens.
|
| 18 |
+
These credentials usually access resources on behalf of a user (resource
|
| 19 |
+
owner).
|
| 20 |
+
|
| 21 |
+
Specifically, this is intended to use access tokens acquired using the
|
| 22 |
+
`Authorization Code grant`_ and can refresh those tokens using a
|
| 23 |
+
optional `refresh token`_.
|
| 24 |
+
|
| 25 |
+
Obtaining the initial access and refresh token is outside of the scope of this
|
| 26 |
+
module. Consult `rfc6749 section 4.1`_ for complete details on the
|
| 27 |
+
Authorization Code grant flow.
|
| 28 |
+
|
| 29 |
+
.. _Authorization Code grant: https://tools.ietf.org/html/rfc6749#section-1.3.1
|
| 30 |
+
.. _refresh token: https://tools.ietf.org/html/rfc6749#section-6
|
| 31 |
+
.. _rfc6749 section 4.1: https://tools.ietf.org/html/rfc6749#section-4.1
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
from datetime import datetime
|
| 35 |
+
import io
|
| 36 |
+
import json
|
| 37 |
+
|
| 38 |
+
import six
|
| 39 |
+
|
| 40 |
+
from google.auth import _cloud_sdk
|
| 41 |
+
from google.auth import _helpers
|
| 42 |
+
from google.auth import credentials
|
| 43 |
+
from google.auth import exceptions
|
| 44 |
+
from google.oauth2 import reauth
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
|
| 48 |
+
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject):
|
| 52 |
+
"""Credentials using OAuth 2.0 access and refresh tokens.
|
| 53 |
+
|
| 54 |
+
The credentials are considered immutable. If you want to modify the
|
| 55 |
+
quota project, use :meth:`with_quota_project` or ::
|
| 56 |
+
|
| 57 |
+
credentials = credentials.with_quota_project('myproject-123')
|
| 58 |
+
|
| 59 |
+
Reauth is disabled by default. To enable reauth, set the
|
| 60 |
+
`enable_reauth_refresh` parameter to True in the constructor. Note that
|
| 61 |
+
reauth feature is intended for gcloud to use only.
|
| 62 |
+
If reauth is enabled, `pyu2f` dependency has to be installed in order to use security
|
| 63 |
+
key reauth feature. Dependency can be installed via `pip install pyu2f` or `pip install
|
| 64 |
+
google-auth[reauth]`.
|
| 65 |
+
"""
|
| 66 |
+
|
| 67 |
+
def __init__(
|
| 68 |
+
self,
|
| 69 |
+
token,
|
| 70 |
+
refresh_token=None,
|
| 71 |
+
id_token=None,
|
| 72 |
+
token_uri=None,
|
| 73 |
+
client_id=None,
|
| 74 |
+
client_secret=None,
|
| 75 |
+
scopes=None,
|
| 76 |
+
default_scopes=None,
|
| 77 |
+
quota_project_id=None,
|
| 78 |
+
expiry=None,
|
| 79 |
+
rapt_token=None,
|
| 80 |
+
refresh_handler=None,
|
| 81 |
+
enable_reauth_refresh=False,
|
| 82 |
+
):
|
| 83 |
+
"""
|
| 84 |
+
Args:
|
| 85 |
+
token (Optional(str)): The OAuth 2.0 access token. Can be None
|
| 86 |
+
if refresh information is provided.
|
| 87 |
+
refresh_token (str): The OAuth 2.0 refresh token. If specified,
|
| 88 |
+
credentials can be refreshed.
|
| 89 |
+
id_token (str): The Open ID Connect ID Token.
|
| 90 |
+
token_uri (str): The OAuth 2.0 authorization server's token
|
| 91 |
+
endpoint URI. Must be specified for refresh, can be left as
|
| 92 |
+
None if the token can not be refreshed.
|
| 93 |
+
client_id (str): The OAuth 2.0 client ID. Must be specified for
|
| 94 |
+
refresh, can be left as None if the token can not be refreshed.
|
| 95 |
+
client_secret(str): The OAuth 2.0 client secret. Must be specified
|
| 96 |
+
for refresh, can be left as None if the token can not be
|
| 97 |
+
refreshed.
|
| 98 |
+
scopes (Sequence[str]): The scopes used to obtain authorization.
|
| 99 |
+
This parameter is used by :meth:`has_scopes`. OAuth 2.0
|
| 100 |
+
credentials can not request additional scopes after
|
| 101 |
+
authorization. The scopes must be derivable from the refresh
|
| 102 |
+
token if refresh information is provided (e.g. The refresh
|
| 103 |
+
token scopes are a superset of this or contain a wild card
|
| 104 |
+
scope like 'https://www.googleapis.com/auth/any-api').
|
| 105 |
+
default_scopes (Sequence[str]): Default scopes passed by a
|
| 106 |
+
Google client library. Use 'scopes' for user-defined scopes.
|
| 107 |
+
quota_project_id (Optional[str]): The project ID used for quota and billing.
|
| 108 |
+
This project may be different from the project used to
|
| 109 |
+
create the credentials.
|
| 110 |
+
rapt_token (Optional[str]): The reauth Proof Token.
|
| 111 |
+
refresh_handler (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
|
| 112 |
+
A callable which takes in the HTTP request callable and the list of
|
| 113 |
+
OAuth scopes and when called returns an access token string for the
|
| 114 |
+
requested scopes and its expiry datetime. This is useful when no
|
| 115 |
+
refresh tokens are provided and tokens are obtained by calling
|
| 116 |
+
some external process on demand. It is particularly useful for
|
| 117 |
+
retrieving downscoped tokens from a token broker.
|
| 118 |
+
enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow
|
| 119 |
+
should be used. This flag is for gcloud to use only.
|
| 120 |
+
"""
|
| 121 |
+
super(Credentials, self).__init__()
|
| 122 |
+
self.token = token
|
| 123 |
+
self.expiry = expiry
|
| 124 |
+
self._refresh_token = refresh_token
|
| 125 |
+
self._id_token = id_token
|
| 126 |
+
self._scopes = scopes
|
| 127 |
+
self._default_scopes = default_scopes
|
| 128 |
+
self._token_uri = token_uri
|
| 129 |
+
self._client_id = client_id
|
| 130 |
+
self._client_secret = client_secret
|
| 131 |
+
self._quota_project_id = quota_project_id
|
| 132 |
+
self._rapt_token = rapt_token
|
| 133 |
+
self.refresh_handler = refresh_handler
|
| 134 |
+
self._enable_reauth_refresh = enable_reauth_refresh
|
| 135 |
+
|
| 136 |
+
def __getstate__(self):
|
| 137 |
+
"""A __getstate__ method must exist for the __setstate__ to be called
|
| 138 |
+
This is identical to the default implementation.
|
| 139 |
+
See https://docs.python.org/3.7/library/pickle.html#object.__setstate__
|
| 140 |
+
"""
|
| 141 |
+
state_dict = self.__dict__.copy()
|
| 142 |
+
# Remove _refresh_handler function as there are limitations pickling and
|
| 143 |
+
# unpickling certain callables (lambda, functools.partial instances)
|
| 144 |
+
# because they need to be importable.
|
| 145 |
+
# Instead, the refresh_handler setter should be used to repopulate this.
|
| 146 |
+
del state_dict["_refresh_handler"]
|
| 147 |
+
return state_dict
|
| 148 |
+
|
| 149 |
+
def __setstate__(self, d):
|
| 150 |
+
"""Credentials pickled with older versions of the class do not have
|
| 151 |
+
all the attributes."""
|
| 152 |
+
self.token = d.get("token")
|
| 153 |
+
self.expiry = d.get("expiry")
|
| 154 |
+
self._refresh_token = d.get("_refresh_token")
|
| 155 |
+
self._id_token = d.get("_id_token")
|
| 156 |
+
self._scopes = d.get("_scopes")
|
| 157 |
+
self._default_scopes = d.get("_default_scopes")
|
| 158 |
+
self._token_uri = d.get("_token_uri")
|
| 159 |
+
self._client_id = d.get("_client_id")
|
| 160 |
+
self._client_secret = d.get("_client_secret")
|
| 161 |
+
self._quota_project_id = d.get("_quota_project_id")
|
| 162 |
+
self._rapt_token = d.get("_rapt_token")
|
| 163 |
+
self._enable_reauth_refresh = d.get("_enable_reauth_refresh")
|
| 164 |
+
# The refresh_handler setter should be used to repopulate this.
|
| 165 |
+
self._refresh_handler = None
|
| 166 |
+
|
| 167 |
+
@property
|
| 168 |
+
def refresh_token(self):
|
| 169 |
+
"""Optional[str]: The OAuth 2.0 refresh token."""
|
| 170 |
+
return self._refresh_token
|
| 171 |
+
|
| 172 |
+
@property
|
| 173 |
+
def scopes(self):
|
| 174 |
+
"""Optional[str]: The OAuth 2.0 permission scopes."""
|
| 175 |
+
return self._scopes
|
| 176 |
+
|
| 177 |
+
@property
|
| 178 |
+
def token_uri(self):
|
| 179 |
+
"""Optional[str]: The OAuth 2.0 authorization server's token endpoint
|
| 180 |
+
URI."""
|
| 181 |
+
return self._token_uri
|
| 182 |
+
|
| 183 |
+
@property
|
| 184 |
+
def id_token(self):
|
| 185 |
+
"""Optional[str]: The Open ID Connect ID Token.
|
| 186 |
+
|
| 187 |
+
Depending on the authorization server and the scopes requested, this
|
| 188 |
+
may be populated when credentials are obtained and updated when
|
| 189 |
+
:meth:`refresh` is called. This token is a JWT. It can be verified
|
| 190 |
+
and decoded using :func:`google.oauth2.id_token.verify_oauth2_token`.
|
| 191 |
+
"""
|
| 192 |
+
return self._id_token
|
| 193 |
+
|
| 194 |
+
@property
|
| 195 |
+
def client_id(self):
|
| 196 |
+
"""Optional[str]: The OAuth 2.0 client ID."""
|
| 197 |
+
return self._client_id
|
| 198 |
+
|
| 199 |
+
@property
|
| 200 |
+
def client_secret(self):
|
| 201 |
+
"""Optional[str]: The OAuth 2.0 client secret."""
|
| 202 |
+
return self._client_secret
|
| 203 |
+
|
| 204 |
+
@property
|
| 205 |
+
def requires_scopes(self):
|
| 206 |
+
"""False: OAuth 2.0 credentials have their scopes set when
|
| 207 |
+
the initial token is requested and can not be changed."""
|
| 208 |
+
return False
|
| 209 |
+
|
| 210 |
+
@property
|
| 211 |
+
def rapt_token(self):
|
| 212 |
+
"""Optional[str]: The reauth Proof Token."""
|
| 213 |
+
return self._rapt_token
|
| 214 |
+
|
| 215 |
+
@property
|
| 216 |
+
def refresh_handler(self):
|
| 217 |
+
"""Returns the refresh handler if available.
|
| 218 |
+
|
| 219 |
+
Returns:
|
| 220 |
+
Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]:
|
| 221 |
+
The current refresh handler.
|
| 222 |
+
"""
|
| 223 |
+
return self._refresh_handler
|
| 224 |
+
|
| 225 |
+
@refresh_handler.setter
|
| 226 |
+
def refresh_handler(self, value):
|
| 227 |
+
"""Updates the current refresh handler.
|
| 228 |
+
|
| 229 |
+
Args:
|
| 230 |
+
value (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
|
| 231 |
+
The updated value of the refresh handler.
|
| 232 |
+
|
| 233 |
+
Raises:
|
| 234 |
+
TypeError: If the value is not a callable or None.
|
| 235 |
+
"""
|
| 236 |
+
if not callable(value) and value is not None:
|
| 237 |
+
raise TypeError("The provided refresh_handler is not a callable or None.")
|
| 238 |
+
self._refresh_handler = value
|
| 239 |
+
|
| 240 |
+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
|
| 241 |
+
def with_quota_project(self, quota_project_id):
|
| 242 |
+
|
| 243 |
+
return self.__class__(
|
| 244 |
+
self.token,
|
| 245 |
+
refresh_token=self.refresh_token,
|
| 246 |
+
id_token=self.id_token,
|
| 247 |
+
token_uri=self.token_uri,
|
| 248 |
+
client_id=self.client_id,
|
| 249 |
+
client_secret=self.client_secret,
|
| 250 |
+
scopes=self.scopes,
|
| 251 |
+
default_scopes=self.default_scopes,
|
| 252 |
+
quota_project_id=quota_project_id,
|
| 253 |
+
rapt_token=self.rapt_token,
|
| 254 |
+
enable_reauth_refresh=self._enable_reauth_refresh,
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
@_helpers.copy_docstring(credentials.Credentials)
|
| 258 |
+
def refresh(self, request):
|
| 259 |
+
scopes = self._scopes if self._scopes is not None else self._default_scopes
|
| 260 |
+
# Use refresh handler if available and no refresh token is
|
| 261 |
+
# available. This is useful in general when tokens are obtained by calling
|
| 262 |
+
# some external process on demand. It is particularly useful for retrieving
|
| 263 |
+
# downscoped tokens from a token broker.
|
| 264 |
+
if self._refresh_token is None and self.refresh_handler:
|
| 265 |
+
token, expiry = self.refresh_handler(request, scopes=scopes)
|
| 266 |
+
# Validate returned data.
|
| 267 |
+
if not isinstance(token, six.string_types):
|
| 268 |
+
raise exceptions.RefreshError(
|
| 269 |
+
"The refresh_handler returned token is not a string."
|
| 270 |
+
)
|
| 271 |
+
if not isinstance(expiry, datetime):
|
| 272 |
+
raise exceptions.RefreshError(
|
| 273 |
+
"The refresh_handler returned expiry is not a datetime object."
|
| 274 |
+
)
|
| 275 |
+
if _helpers.utcnow() >= expiry - _helpers.REFRESH_THRESHOLD:
|
| 276 |
+
raise exceptions.RefreshError(
|
| 277 |
+
"The credentials returned by the refresh_handler are "
|
| 278 |
+
"already expired."
|
| 279 |
+
)
|
| 280 |
+
self.token = token
|
| 281 |
+
self.expiry = expiry
|
| 282 |
+
return
|
| 283 |
+
|
| 284 |
+
if (
|
| 285 |
+
self._refresh_token is None
|
| 286 |
+
or self._token_uri is None
|
| 287 |
+
or self._client_id is None
|
| 288 |
+
or self._client_secret is None
|
| 289 |
+
):
|
| 290 |
+
raise exceptions.RefreshError(
|
| 291 |
+
"The credentials do not contain the necessary fields need to "
|
| 292 |
+
"refresh the access token. You must specify refresh_token, "
|
| 293 |
+
"token_uri, client_id, and client_secret."
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
(
|
| 297 |
+
access_token,
|
| 298 |
+
refresh_token,
|
| 299 |
+
expiry,
|
| 300 |
+
grant_response,
|
| 301 |
+
rapt_token,
|
| 302 |
+
) = reauth.refresh_grant(
|
| 303 |
+
request,
|
| 304 |
+
self._token_uri,
|
| 305 |
+
self._refresh_token,
|
| 306 |
+
self._client_id,
|
| 307 |
+
self._client_secret,
|
| 308 |
+
scopes=scopes,
|
| 309 |
+
rapt_token=self._rapt_token,
|
| 310 |
+
enable_reauth_refresh=self._enable_reauth_refresh,
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
self.token = access_token
|
| 314 |
+
self.expiry = expiry
|
| 315 |
+
self._refresh_token = refresh_token
|
| 316 |
+
self._id_token = grant_response.get("id_token")
|
| 317 |
+
self._rapt_token = rapt_token
|
| 318 |
+
|
| 319 |
+
if scopes and "scope" in grant_response:
|
| 320 |
+
requested_scopes = frozenset(scopes)
|
| 321 |
+
granted_scopes = frozenset(grant_response["scope"].split())
|
| 322 |
+
scopes_requested_but_not_granted = requested_scopes - granted_scopes
|
| 323 |
+
if scopes_requested_but_not_granted:
|
| 324 |
+
raise exceptions.RefreshError(
|
| 325 |
+
"Not all requested scopes were granted by the "
|
| 326 |
+
"authorization server, missing scopes {}.".format(
|
| 327 |
+
", ".join(scopes_requested_but_not_granted)
|
| 328 |
+
)
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
@classmethod
|
| 332 |
+
def from_authorized_user_info(cls, info, scopes=None):
|
| 333 |
+
"""Creates a Credentials instance from parsed authorized user info.
|
| 334 |
+
|
| 335 |
+
Args:
|
| 336 |
+
info (Mapping[str, str]): The authorized user info in Google
|
| 337 |
+
format.
|
| 338 |
+
scopes (Sequence[str]): Optional list of scopes to include in the
|
| 339 |
+
credentials.
|
| 340 |
+
|
| 341 |
+
Returns:
|
| 342 |
+
google.oauth2.credentials.Credentials: The constructed
|
| 343 |
+
credentials.
|
| 344 |
+
|
| 345 |
+
Raises:
|
| 346 |
+
ValueError: If the info is not in the expected format.
|
| 347 |
+
"""
|
| 348 |
+
keys_needed = set(("refresh_token", "client_id", "client_secret"))
|
| 349 |
+
missing = keys_needed.difference(six.iterkeys(info))
|
| 350 |
+
|
| 351 |
+
if missing:
|
| 352 |
+
raise ValueError(
|
| 353 |
+
"Authorized user info was not in the expected format, missing "
|
| 354 |
+
"fields {}.".format(", ".join(missing))
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
# access token expiry (datetime obj); auto-expire if not saved
|
| 358 |
+
expiry = info.get("expiry")
|
| 359 |
+
if expiry:
|
| 360 |
+
expiry = datetime.strptime(
|
| 361 |
+
expiry.rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S"
|
| 362 |
+
)
|
| 363 |
+
else:
|
| 364 |
+
expiry = _helpers.utcnow() - _helpers.REFRESH_THRESHOLD
|
| 365 |
+
|
| 366 |
+
# process scopes, which needs to be a seq
|
| 367 |
+
if scopes is None and "scopes" in info:
|
| 368 |
+
scopes = info.get("scopes")
|
| 369 |
+
if isinstance(scopes, six.string_types):
|
| 370 |
+
scopes = scopes.split(" ")
|
| 371 |
+
|
| 372 |
+
return cls(
|
| 373 |
+
token=info.get("token"),
|
| 374 |
+
refresh_token=info.get("refresh_token"),
|
| 375 |
+
token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, # always overrides
|
| 376 |
+
scopes=scopes,
|
| 377 |
+
client_id=info.get("client_id"),
|
| 378 |
+
client_secret=info.get("client_secret"),
|
| 379 |
+
quota_project_id=info.get("quota_project_id"), # may not exist
|
| 380 |
+
expiry=expiry,
|
| 381 |
+
rapt_token=info.get("rapt_token"), # may not exist
|
| 382 |
+
)
|
| 383 |
+
|
| 384 |
+
@classmethod
|
| 385 |
+
def from_authorized_user_file(cls, filename, scopes=None):
|
| 386 |
+
"""Creates a Credentials instance from an authorized user json file.
|
| 387 |
+
|
| 388 |
+
Args:
|
| 389 |
+
filename (str): The path to the authorized user json file.
|
| 390 |
+
scopes (Sequence[str]): Optional list of scopes to include in the
|
| 391 |
+
credentials.
|
| 392 |
+
|
| 393 |
+
Returns:
|
| 394 |
+
google.oauth2.credentials.Credentials: The constructed
|
| 395 |
+
credentials.
|
| 396 |
+
|
| 397 |
+
Raises:
|
| 398 |
+
ValueError: If the file is not in the expected format.
|
| 399 |
+
"""
|
| 400 |
+
with io.open(filename, "r", encoding="utf-8") as json_file:
|
| 401 |
+
data = json.load(json_file)
|
| 402 |
+
return cls.from_authorized_user_info(data, scopes)
|
| 403 |
+
|
| 404 |
+
def to_json(self, strip=None):
|
| 405 |
+
"""Utility function that creates a JSON representation of a Credentials
|
| 406 |
+
object.
|
| 407 |
+
|
| 408 |
+
Args:
|
| 409 |
+
strip (Sequence[str]): Optional list of members to exclude from the
|
| 410 |
+
generated JSON.
|
| 411 |
+
|
| 412 |
+
Returns:
|
| 413 |
+
str: A JSON representation of this instance. When converted into
|
| 414 |
+
a dictionary, it can be passed to from_authorized_user_info()
|
| 415 |
+
to create a new credential instance.
|
| 416 |
+
"""
|
| 417 |
+
prep = {
|
| 418 |
+
"token": self.token,
|
| 419 |
+
"refresh_token": self.refresh_token,
|
| 420 |
+
"token_uri": self.token_uri,
|
| 421 |
+
"client_id": self.client_id,
|
| 422 |
+
"client_secret": self.client_secret,
|
| 423 |
+
"scopes": self.scopes,
|
| 424 |
+
"rapt_token": self.rapt_token,
|
| 425 |
+
}
|
| 426 |
+
if self.expiry: # flatten expiry timestamp
|
| 427 |
+
prep["expiry"] = self.expiry.isoformat() + "Z"
|
| 428 |
+
|
| 429 |
+
# Remove empty entries (those which are None)
|
| 430 |
+
prep = {k: v for k, v in prep.items() if v is not None}
|
| 431 |
+
|
| 432 |
+
# Remove entries that explicitely need to be removed
|
| 433 |
+
if strip is not None:
|
| 434 |
+
prep = {k: v for k, v in prep.items() if k not in strip}
|
| 435 |
+
|
| 436 |
+
return json.dumps(prep)
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
class UserAccessTokenCredentials(credentials.CredentialsWithQuotaProject):
|
| 440 |
+
"""Access token credentials for user account.
|
| 441 |
+
|
| 442 |
+
Obtain the access token for a given user account or the current active
|
| 443 |
+
user account with the ``gcloud auth print-access-token`` command.
|
| 444 |
+
|
| 445 |
+
Args:
|
| 446 |
+
account (Optional[str]): Account to get the access token for. If not
|
| 447 |
+
specified, the current active account will be used.
|
| 448 |
+
quota_project_id (Optional[str]): The project ID used for quota
|
| 449 |
+
and billing.
|
| 450 |
+
"""
|
| 451 |
+
|
| 452 |
+
def __init__(self, account=None, quota_project_id=None):
|
| 453 |
+
super(UserAccessTokenCredentials, self).__init__()
|
| 454 |
+
self._account = account
|
| 455 |
+
self._quota_project_id = quota_project_id
|
| 456 |
+
|
| 457 |
+
def with_account(self, account):
|
| 458 |
+
"""Create a new instance with the given account.
|
| 459 |
+
|
| 460 |
+
Args:
|
| 461 |
+
account (str): Account to get the access token for.
|
| 462 |
+
|
| 463 |
+
Returns:
|
| 464 |
+
google.oauth2.credentials.UserAccessTokenCredentials: The created
|
| 465 |
+
credentials with the given account.
|
| 466 |
+
"""
|
| 467 |
+
return self.__class__(account=account, quota_project_id=self._quota_project_id)
|
| 468 |
+
|
| 469 |
+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
|
| 470 |
+
def with_quota_project(self, quota_project_id):
|
| 471 |
+
return self.__class__(account=self._account, quota_project_id=quota_project_id)
|
| 472 |
+
|
| 473 |
+
def refresh(self, request):
|
| 474 |
+
"""Refreshes the access token.
|
| 475 |
+
|
| 476 |
+
Args:
|
| 477 |
+
request (google.auth.transport.Request): This argument is required
|
| 478 |
+
by the base class interface but not used in this implementation,
|
| 479 |
+
so just set it to `None`.
|
| 480 |
+
|
| 481 |
+
Raises:
|
| 482 |
+
google.auth.exceptions.UserAccessTokenError: If the access token
|
| 483 |
+
refresh failed.
|
| 484 |
+
"""
|
| 485 |
+
self.token = _cloud_sdk.get_auth_access_token(self._account)
|
| 486 |
+
|
| 487 |
+
@_helpers.copy_docstring(credentials.Credentials)
|
| 488 |
+
def before_request(self, request, method, url, headers):
|
| 489 |
+
self.refresh(request)
|
| 490 |
+
self.apply(headers)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/gdch_credentials.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Experimental GDCH credentials support.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
import datetime
|
| 19 |
+
|
| 20 |
+
from google.auth import _helpers
|
| 21 |
+
from google.auth import _service_account_info
|
| 22 |
+
from google.auth import credentials
|
| 23 |
+
from google.auth import exceptions
|
| 24 |
+
from google.auth import jwt
|
| 25 |
+
from google.oauth2 import _client
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
TOKEN_EXCHANGE_TYPE = "urn:ietf:params:oauth:token-type:token-exchange"
|
| 29 |
+
ACCESS_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
|
| 30 |
+
SERVICE_ACCOUNT_TOKEN_TYPE = "urn:k8s:params:oauth:token-type:serviceaccount"
|
| 31 |
+
JWT_LIFETIME = datetime.timedelta(seconds=3600) # 1 hour
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class ServiceAccountCredentials(credentials.Credentials):
|
| 35 |
+
"""Credentials for GDCH (`Google Distributed Cloud Hosted`_) for service
|
| 36 |
+
account users.
|
| 37 |
+
|
| 38 |
+
.. _Google Distributed Cloud Hosted:
|
| 39 |
+
https://cloud.google.com/blog/topics/hybrid-cloud/\
|
| 40 |
+
announcing-google-distributed-cloud-edge-and-hosted
|
| 41 |
+
|
| 42 |
+
To create a GDCH service account credential, first create a JSON file of
|
| 43 |
+
the following format::
|
| 44 |
+
|
| 45 |
+
{
|
| 46 |
+
"type": "gdch_service_account",
|
| 47 |
+
"format_version": "1",
|
| 48 |
+
"project": "<project name>",
|
| 49 |
+
"private_key_id": "<key id>",
|
| 50 |
+
"private_key": "-----BEGIN EC PRIVATE KEY-----\n<key bytes>\n-----END EC PRIVATE KEY-----\n",
|
| 51 |
+
"name": "<service identity name>",
|
| 52 |
+
"ca_cert_path": "<CA cert path>",
|
| 53 |
+
"token_uri": "https://service-identity.<Domain>/authenticate"
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
The "format_version" field stands for the format of the JSON file. For now
|
| 57 |
+
it is always "1". The `private_key_id` and `private_key` is used for signing.
|
| 58 |
+
The `ca_cert_path` is used for token server TLS certificate verification.
|
| 59 |
+
|
| 60 |
+
After the JSON file is created, set `GOOGLE_APPLICATION_CREDENTIALS` environment
|
| 61 |
+
variable to the JSON file path, then use the following code to create the
|
| 62 |
+
credential::
|
| 63 |
+
|
| 64 |
+
import google.auth
|
| 65 |
+
|
| 66 |
+
credential, _ = google.auth.default()
|
| 67 |
+
credential = credential.with_gdch_audience("<the audience>")
|
| 68 |
+
|
| 69 |
+
We can also create the credential directly::
|
| 70 |
+
|
| 71 |
+
from google.oauth import gdch_credentials
|
| 72 |
+
|
| 73 |
+
credential = gdch_credentials.ServiceAccountCredentials.from_service_account_file("<the json file path>")
|
| 74 |
+
credential = credential.with_gdch_audience("<the audience>")
|
| 75 |
+
|
| 76 |
+
The token is obtained in the following way. This class first creates a
|
| 77 |
+
self signed JWT. It uses the `name` value as the `iss` and `sub` claim, and
|
| 78 |
+
the `token_uri` as the `aud` claim, and signs the JWT with the `private_key`.
|
| 79 |
+
It then sends the JWT to the `token_uri` to exchange a final token for
|
| 80 |
+
`audience`.
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
def __init__(
|
| 84 |
+
self, signer, service_identity_name, project, audience, token_uri, ca_cert_path
|
| 85 |
+
):
|
| 86 |
+
"""
|
| 87 |
+
Args:
|
| 88 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 89 |
+
service_identity_name (str): The service identity name. It will be
|
| 90 |
+
used as the `iss` and `sub` claim in the self signed JWT.
|
| 91 |
+
project (str): The project.
|
| 92 |
+
audience (str): The audience for the final token.
|
| 93 |
+
token_uri (str): The token server uri.
|
| 94 |
+
ca_cert_path (str): The CA cert path for token server side TLS
|
| 95 |
+
certificate verification. If the token server uses well known
|
| 96 |
+
CA, then this parameter can be `None`.
|
| 97 |
+
"""
|
| 98 |
+
super(ServiceAccountCredentials, self).__init__()
|
| 99 |
+
self._signer = signer
|
| 100 |
+
self._service_identity_name = service_identity_name
|
| 101 |
+
self._project = project
|
| 102 |
+
self._audience = audience
|
| 103 |
+
self._token_uri = token_uri
|
| 104 |
+
self._ca_cert_path = ca_cert_path
|
| 105 |
+
|
| 106 |
+
def _create_jwt(self):
|
| 107 |
+
now = _helpers.utcnow()
|
| 108 |
+
expiry = now + JWT_LIFETIME
|
| 109 |
+
iss_sub_value = "system:serviceaccount:{}:{}".format(
|
| 110 |
+
self._project, self._service_identity_name
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
payload = {
|
| 114 |
+
"iss": iss_sub_value,
|
| 115 |
+
"sub": iss_sub_value,
|
| 116 |
+
"aud": self._token_uri,
|
| 117 |
+
"iat": _helpers.datetime_to_secs(now),
|
| 118 |
+
"exp": _helpers.datetime_to_secs(expiry),
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
return _helpers.from_bytes(jwt.encode(self._signer, payload))
|
| 122 |
+
|
| 123 |
+
@_helpers.copy_docstring(credentials.Credentials)
|
| 124 |
+
def refresh(self, request):
|
| 125 |
+
import google.auth.transport.requests
|
| 126 |
+
|
| 127 |
+
if not isinstance(request, google.auth.transport.requests.Request):
|
| 128 |
+
raise exceptions.RefreshError(
|
| 129 |
+
"For GDCH service account credentials, request must be a google.auth.transport.requests.Request object"
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# Create a self signed JWT, and do token exchange.
|
| 133 |
+
jwt_token = self._create_jwt()
|
| 134 |
+
request_body = {
|
| 135 |
+
"grant_type": TOKEN_EXCHANGE_TYPE,
|
| 136 |
+
"audience": self._audience,
|
| 137 |
+
"requested_token_type": ACCESS_TOKEN_TOKEN_TYPE,
|
| 138 |
+
"subject_token": jwt_token,
|
| 139 |
+
"subject_token_type": SERVICE_ACCOUNT_TOKEN_TYPE,
|
| 140 |
+
}
|
| 141 |
+
response_data = _client._token_endpoint_request(
|
| 142 |
+
request,
|
| 143 |
+
self._token_uri,
|
| 144 |
+
request_body,
|
| 145 |
+
access_token=None,
|
| 146 |
+
use_json=True,
|
| 147 |
+
verify=self._ca_cert_path,
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
self.token, _, self.expiry, _ = _client._handle_refresh_grant_response(
|
| 151 |
+
response_data, None
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
def with_gdch_audience(self, audience):
|
| 155 |
+
"""Create a copy of GDCH credentials with the specified audience.
|
| 156 |
+
|
| 157 |
+
Args:
|
| 158 |
+
audience (str): The intended audience for GDCH credentials.
|
| 159 |
+
"""
|
| 160 |
+
return self.__class__(
|
| 161 |
+
self._signer,
|
| 162 |
+
self._service_identity_name,
|
| 163 |
+
self._project,
|
| 164 |
+
audience,
|
| 165 |
+
self._token_uri,
|
| 166 |
+
self._ca_cert_path,
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
@classmethod
|
| 170 |
+
def _from_signer_and_info(cls, signer, info):
|
| 171 |
+
"""Creates a Credentials instance from a signer and service account
|
| 172 |
+
info.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
|
| 176 |
+
info (Mapping[str, str]): The service account info.
|
| 177 |
+
|
| 178 |
+
Returns:
|
| 179 |
+
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
|
| 180 |
+
credentials.
|
| 181 |
+
|
| 182 |
+
Raises:
|
| 183 |
+
ValueError: If the info is not in the expected format.
|
| 184 |
+
"""
|
| 185 |
+
if info["format_version"] != "1":
|
| 186 |
+
raise ValueError("Only format version 1 is supported")
|
| 187 |
+
|
| 188 |
+
return cls(
|
| 189 |
+
signer,
|
| 190 |
+
info["name"], # service_identity_name
|
| 191 |
+
info["project"],
|
| 192 |
+
None, # audience
|
| 193 |
+
info["token_uri"],
|
| 194 |
+
info.get("ca_cert_path", None),
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
@classmethod
|
| 198 |
+
def from_service_account_info(cls, info):
|
| 199 |
+
"""Creates a Credentials instance from parsed service account info.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
info (Mapping[str, str]): The service account info in Google
|
| 203 |
+
format.
|
| 204 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 205 |
+
|
| 206 |
+
Returns:
|
| 207 |
+
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
|
| 208 |
+
credentials.
|
| 209 |
+
|
| 210 |
+
Raises:
|
| 211 |
+
ValueError: If the info is not in the expected format.
|
| 212 |
+
"""
|
| 213 |
+
signer = _service_account_info.from_dict(
|
| 214 |
+
info,
|
| 215 |
+
require=[
|
| 216 |
+
"format_version",
|
| 217 |
+
"private_key_id",
|
| 218 |
+
"private_key",
|
| 219 |
+
"name",
|
| 220 |
+
"project",
|
| 221 |
+
"token_uri",
|
| 222 |
+
],
|
| 223 |
+
use_rsa_signer=False,
|
| 224 |
+
)
|
| 225 |
+
return cls._from_signer_and_info(signer, info)
|
| 226 |
+
|
| 227 |
+
@classmethod
|
| 228 |
+
def from_service_account_file(cls, filename):
|
| 229 |
+
"""Creates a Credentials instance from a service account json file.
|
| 230 |
+
|
| 231 |
+
Args:
|
| 232 |
+
filename (str): The path to the service account json file.
|
| 233 |
+
kwargs: Additional arguments to pass to the constructor.
|
| 234 |
+
|
| 235 |
+
Returns:
|
| 236 |
+
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
|
| 237 |
+
credentials.
|
| 238 |
+
"""
|
| 239 |
+
info, signer = _service_account_info.from_filename(
|
| 240 |
+
filename,
|
| 241 |
+
require=[
|
| 242 |
+
"format_version",
|
| 243 |
+
"private_key_id",
|
| 244 |
+
"private_key",
|
| 245 |
+
"name",
|
| 246 |
+
"project",
|
| 247 |
+
"token_uri",
|
| 248 |
+
],
|
| 249 |
+
use_rsa_signer=False,
|
| 250 |
+
)
|
| 251 |
+
return cls._from_signer_and_info(signer, info)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/oauth2/id_token.py
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2016 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
"""Google ID Token helpers.
|
| 16 |
+
|
| 17 |
+
Provides support for verifying `OpenID Connect ID Tokens`_, especially ones
|
| 18 |
+
generated by Google infrastructure.
|
| 19 |
+
|
| 20 |
+
To parse and verify an ID Token issued by Google's OAuth 2.0 authorization
|
| 21 |
+
server use :func:`verify_oauth2_token`. To verify an ID Token issued by
|
| 22 |
+
Firebase, use :func:`verify_firebase_token`.
|
| 23 |
+
|
| 24 |
+
A general purpose ID Token verifier is available as :func:`verify_token`.
|
| 25 |
+
|
| 26 |
+
Example::
|
| 27 |
+
|
| 28 |
+
from google.oauth2 import id_token
|
| 29 |
+
from google.auth.transport import requests
|
| 30 |
+
|
| 31 |
+
request = requests.Request()
|
| 32 |
+
|
| 33 |
+
id_info = id_token.verify_oauth2_token(
|
| 34 |
+
token, request, 'my-client-id.example.com')
|
| 35 |
+
|
| 36 |
+
userid = id_info['sub']
|
| 37 |
+
|
| 38 |
+
By default, this will re-fetch certificates for each verification. Because
|
| 39 |
+
Google's public keys are only changed infrequently (on the order of once per
|
| 40 |
+
day), you may wish to take advantage of caching to reduce latency and the
|
| 41 |
+
potential for network errors. This can be accomplished using an external
|
| 42 |
+
library like `CacheControl`_ to create a cache-aware
|
| 43 |
+
:class:`google.auth.transport.Request`::
|
| 44 |
+
|
| 45 |
+
import cachecontrol
|
| 46 |
+
import google.auth.transport.requests
|
| 47 |
+
import requests
|
| 48 |
+
|
| 49 |
+
session = requests.session()
|
| 50 |
+
cached_session = cachecontrol.CacheControl(session)
|
| 51 |
+
request = google.auth.transport.requests.Request(session=cached_session)
|
| 52 |
+
|
| 53 |
+
.. _OpenID Connect ID Tokens:
|
| 54 |
+
http://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
| 55 |
+
.. _CacheControl: https://cachecontrol.readthedocs.io
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
import json
|
| 59 |
+
import os
|
| 60 |
+
|
| 61 |
+
import six
|
| 62 |
+
from six.moves import http_client
|
| 63 |
+
|
| 64 |
+
from google.auth import environment_vars
|
| 65 |
+
from google.auth import exceptions
|
| 66 |
+
from google.auth import jwt
|
| 67 |
+
import google.auth.transport.requests
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# The URL that provides public certificates for verifying ID tokens issued
|
| 71 |
+
# by Google's OAuth 2.0 authorization server.
|
| 72 |
+
_GOOGLE_OAUTH2_CERTS_URL = "https://www.googleapis.com/oauth2/v1/certs"
|
| 73 |
+
|
| 74 |
+
# The URL that provides public certificates for verifying ID tokens issued
|
| 75 |
+
# by Firebase and the Google APIs infrastructure
|
| 76 |
+
_GOOGLE_APIS_CERTS_URL = (
|
| 77 |
+
"https://www.googleapis.com/robot/v1/metadata/x509"
|
| 78 |
+
"/securetoken@system.gserviceaccount.com"
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
_GOOGLE_ISSUERS = ["accounts.google.com", "https://accounts.google.com"]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _fetch_certs(request, certs_url):
|
| 85 |
+
"""Fetches certificates.
|
| 86 |
+
|
| 87 |
+
Google-style cerificate endpoints return JSON in the format of
|
| 88 |
+
``{'key id': 'x509 certificate'}``.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
request (google.auth.transport.Request): The object used to make
|
| 92 |
+
HTTP requests.
|
| 93 |
+
certs_url (str): The certificate endpoint URL.
|
| 94 |
+
|
| 95 |
+
Returns:
|
| 96 |
+
Mapping[str, str]: A mapping of public key ID to x.509 certificate
|
| 97 |
+
data.
|
| 98 |
+
"""
|
| 99 |
+
response = request(certs_url, method="GET")
|
| 100 |
+
|
| 101 |
+
if response.status != http_client.OK:
|
| 102 |
+
raise exceptions.TransportError(
|
| 103 |
+
"Could not fetch certificates at {}".format(certs_url)
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
return json.loads(response.data.decode("utf-8"))
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def verify_token(
|
| 110 |
+
id_token,
|
| 111 |
+
request,
|
| 112 |
+
audience=None,
|
| 113 |
+
certs_url=_GOOGLE_OAUTH2_CERTS_URL,
|
| 114 |
+
clock_skew_in_seconds=0,
|
| 115 |
+
):
|
| 116 |
+
"""Verifies an ID token and returns the decoded token.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 120 |
+
request (google.auth.transport.Request): The object used to make
|
| 121 |
+
HTTP requests.
|
| 122 |
+
audience (str or list): The audience or audiences that this token is
|
| 123 |
+
intended for. If None then the audience is not verified.
|
| 124 |
+
certs_url (str): The URL that specifies the certificates to use to
|
| 125 |
+
verify the token. This URL should return JSON in the format of
|
| 126 |
+
``{'key id': 'x509 certificate'}``.
|
| 127 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 128 |
+
validation.
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
Mapping[str, Any]: The decoded token.
|
| 132 |
+
"""
|
| 133 |
+
certs = _fetch_certs(request, certs_url)
|
| 134 |
+
|
| 135 |
+
return jwt.decode(
|
| 136 |
+
id_token,
|
| 137 |
+
certs=certs,
|
| 138 |
+
audience=audience,
|
| 139 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def verify_oauth2_token(id_token, request, audience=None, clock_skew_in_seconds=0):
|
| 144 |
+
"""Verifies an ID Token issued by Google's OAuth 2.0 authorization server.
|
| 145 |
+
|
| 146 |
+
Args:
|
| 147 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 148 |
+
request (google.auth.transport.Request): The object used to make
|
| 149 |
+
HTTP requests.
|
| 150 |
+
audience (str): The audience that this token is intended for. This is
|
| 151 |
+
typically your application's OAuth 2.0 client ID. If None then the
|
| 152 |
+
audience is not verified.
|
| 153 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 154 |
+
validation.
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
Mapping[str, Any]: The decoded token.
|
| 158 |
+
|
| 159 |
+
Raises:
|
| 160 |
+
exceptions.GoogleAuthError: If the issuer is invalid.
|
| 161 |
+
ValueError: If token verification fails
|
| 162 |
+
"""
|
| 163 |
+
idinfo = verify_token(
|
| 164 |
+
id_token,
|
| 165 |
+
request,
|
| 166 |
+
audience=audience,
|
| 167 |
+
certs_url=_GOOGLE_OAUTH2_CERTS_URL,
|
| 168 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
if idinfo["iss"] not in _GOOGLE_ISSUERS:
|
| 172 |
+
raise exceptions.GoogleAuthError(
|
| 173 |
+
"Wrong issuer. 'iss' should be one of the following: {}".format(
|
| 174 |
+
_GOOGLE_ISSUERS
|
| 175 |
+
)
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
return idinfo
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def verify_firebase_token(id_token, request, audience=None, clock_skew_in_seconds=0):
|
| 182 |
+
"""Verifies an ID Token issued by Firebase Authentication.
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
id_token (Union[str, bytes]): The encoded token.
|
| 186 |
+
request (google.auth.transport.Request): The object used to make
|
| 187 |
+
HTTP requests.
|
| 188 |
+
audience (str): The audience that this token is intended for. This is
|
| 189 |
+
typically your Firebase application ID. If None then the audience
|
| 190 |
+
is not verified.
|
| 191 |
+
clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
|
| 192 |
+
validation.
|
| 193 |
+
|
| 194 |
+
Returns:
|
| 195 |
+
Mapping[str, Any]: The decoded token.
|
| 196 |
+
"""
|
| 197 |
+
return verify_token(
|
| 198 |
+
id_token,
|
| 199 |
+
request,
|
| 200 |
+
audience=audience,
|
| 201 |
+
certs_url=_GOOGLE_APIS_CERTS_URL,
|
| 202 |
+
clock_skew_in_seconds=clock_skew_in_seconds,
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def fetch_id_token_credentials(audience, request=None):
|
| 207 |
+
"""Create the ID Token credentials from the current environment.
|
| 208 |
+
|
| 209 |
+
This function acquires ID token from the environment in the following order.
|
| 210 |
+
See https://google.aip.dev/auth/4110.
|
| 211 |
+
|
| 212 |
+
1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
|
| 213 |
+
to the path of a valid service account JSON file, then ID token is
|
| 214 |
+
acquired using this service account credentials.
|
| 215 |
+
2. If the application is running in Compute Engine, App Engine or Cloud Run,
|
| 216 |
+
then the ID token are obtained from the metadata server.
|
| 217 |
+
3. If metadata server doesn't exist and no valid service account credentials
|
| 218 |
+
are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
|
| 219 |
+
be raised.
|
| 220 |
+
|
| 221 |
+
Example::
|
| 222 |
+
|
| 223 |
+
import google.oauth2.id_token
|
| 224 |
+
import google.auth.transport.requests
|
| 225 |
+
|
| 226 |
+
request = google.auth.transport.requests.Request()
|
| 227 |
+
target_audience = "https://pubsub.googleapis.com"
|
| 228 |
+
|
| 229 |
+
# Create ID token credentials.
|
| 230 |
+
credentials = google.oauth2.id_token.fetch_id_token_credentials(target_audience, request=request)
|
| 231 |
+
|
| 232 |
+
# Refresh the credential to obtain an ID token.
|
| 233 |
+
credentials.refresh(request)
|
| 234 |
+
|
| 235 |
+
id_token = credentials.token
|
| 236 |
+
id_token_expiry = credentials.expiry
|
| 237 |
+
|
| 238 |
+
Args:
|
| 239 |
+
audience (str): The audience that this ID token is intended for.
|
| 240 |
+
request (Optional[google.auth.transport.Request]): A callable used to make
|
| 241 |
+
HTTP requests. A request object will be created if not provided.
|
| 242 |
+
|
| 243 |
+
Returns:
|
| 244 |
+
google.auth.credentials.Credentials: The ID token credentials.
|
| 245 |
+
|
| 246 |
+
Raises:
|
| 247 |
+
~google.auth.exceptions.DefaultCredentialsError:
|
| 248 |
+
If metadata server doesn't exist and no valid service account
|
| 249 |
+
credentials are found.
|
| 250 |
+
"""
|
| 251 |
+
# 1. Try to get credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
|
| 252 |
+
# variable.
|
| 253 |
+
credentials_filename = os.environ.get(environment_vars.CREDENTIALS)
|
| 254 |
+
if credentials_filename:
|
| 255 |
+
if not (
|
| 256 |
+
os.path.exists(credentials_filename)
|
| 257 |
+
and os.path.isfile(credentials_filename)
|
| 258 |
+
):
|
| 259 |
+
raise exceptions.DefaultCredentialsError(
|
| 260 |
+
"GOOGLE_APPLICATION_CREDENTIALS path is either not found or invalid."
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
try:
|
| 264 |
+
with open(credentials_filename, "r") as f:
|
| 265 |
+
from google.oauth2 import service_account
|
| 266 |
+
|
| 267 |
+
info = json.load(f)
|
| 268 |
+
if info.get("type") == "service_account":
|
| 269 |
+
return service_account.IDTokenCredentials.from_service_account_info(
|
| 270 |
+
info, target_audience=audience
|
| 271 |
+
)
|
| 272 |
+
except ValueError as caught_exc:
|
| 273 |
+
new_exc = exceptions.DefaultCredentialsError(
|
| 274 |
+
"GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
|
| 275 |
+
caught_exc,
|
| 276 |
+
)
|
| 277 |
+
six.raise_from(new_exc, caught_exc)
|
| 278 |
+
|
| 279 |
+
# 2. Try to fetch ID token from metada server if it exists. The code
|
| 280 |
+
# works for GAE and Cloud Run metadata server as well.
|
| 281 |
+
try:
|
| 282 |
+
from google.auth import compute_engine
|
| 283 |
+
from google.auth.compute_engine import _metadata
|
| 284 |
+
|
| 285 |
+
# Create a request object if not provided.
|
| 286 |
+
if not request:
|
| 287 |
+
request = google.auth.transport.requests.Request()
|
| 288 |
+
|
| 289 |
+
if _metadata.ping(request):
|
| 290 |
+
return compute_engine.IDTokenCredentials(
|
| 291 |
+
request, audience, use_metadata_identity_endpoint=True
|
| 292 |
+
)
|
| 293 |
+
except (ImportError, exceptions.TransportError):
|
| 294 |
+
pass
|
| 295 |
+
|
| 296 |
+
raise exceptions.DefaultCredentialsError(
|
| 297 |
+
"Neither metadata server or valid service account credentials are found."
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def fetch_id_token(request, audience):
|
| 302 |
+
"""Fetch the ID Token from the current environment.
|
| 303 |
+
|
| 304 |
+
This function acquires ID token from the environment in the following order.
|
| 305 |
+
See https://google.aip.dev/auth/4110.
|
| 306 |
+
|
| 307 |
+
1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
|
| 308 |
+
to the path of a valid service account JSON file, then ID token is
|
| 309 |
+
acquired using this service account credentials.
|
| 310 |
+
2. If the application is running in Compute Engine, App Engine or Cloud Run,
|
| 311 |
+
then the ID token are obtained from the metadata server.
|
| 312 |
+
3. If metadata server doesn't exist and no valid service account credentials
|
| 313 |
+
are found, :class:`~google.auth.exceptions.DefaultCredentialsError` will
|
| 314 |
+
be raised.
|
| 315 |
+
|
| 316 |
+
Example::
|
| 317 |
+
|
| 318 |
+
import google.oauth2.id_token
|
| 319 |
+
import google.auth.transport.requests
|
| 320 |
+
|
| 321 |
+
request = google.auth.transport.requests.Request()
|
| 322 |
+
target_audience = "https://pubsub.googleapis.com"
|
| 323 |
+
|
| 324 |
+
id_token = google.oauth2.id_token.fetch_id_token(request, target_audience)
|
| 325 |
+
|
| 326 |
+
Args:
|
| 327 |
+
request (google.auth.transport.Request): A callable used to make
|
| 328 |
+
HTTP requests.
|
| 329 |
+
audience (str): The audience that this ID token is intended for.
|
| 330 |
+
|
| 331 |
+
Returns:
|
| 332 |
+
str: The ID token.
|
| 333 |
+
|
| 334 |
+
Raises:
|
| 335 |
+
~google.auth.exceptions.DefaultCredentialsError:
|
| 336 |
+
If metadata server doesn't exist and no valid service account
|
| 337 |
+
credentials are found.
|
| 338 |
+
"""
|
| 339 |
+
id_token_credentials = fetch_id_token_credentials(audience, request=request)
|
| 340 |
+
id_token_credentials.refresh(request)
|
| 341 |
+
return id_token_credentials.token
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor.cpython-38.pyc
ADDED
|
Binary file (36.8 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-38.pyc
ADDED
|
Binary file (34.5 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-38.pyc
ADDED
|
Binary file (1.2 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/message.cpython-38.pyc
ADDED
|
Binary file (13 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/service_reflection.cpython-38.pyc
ADDED
|
Binary file (10.5 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-38.pyc
ADDED
|
Binary file (1.23 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-38.pyc
ADDED
|
Binary file (1.21 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/__pycache__/type_pb2.cpython-38.pyc
ADDED
|
Binary file (2.94 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/compiler/__init__.py
ADDED
|
File without changes
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/compiler/plugin_pb2.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
| 3 |
+
# source: google/protobuf/compiler/plugin.proto
|
| 4 |
+
"""Generated protocol buffer code."""
|
| 5 |
+
from google.protobuf.internal import builder as _builder
|
| 6 |
+
from google.protobuf import descriptor as _descriptor
|
| 7 |
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
| 8 |
+
from google.protobuf import symbol_database as _symbol_database
|
| 9 |
+
# @@protoc_insertion_point(imports)
|
| 10 |
+
|
| 11 |
+
_sym_db = _symbol_database.Default()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%google/protobuf/compiler/plugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"F\n\x07Version\x12\r\n\x05major\x18\x01 \x01(\x05\x12\r\n\x05minor\x18\x02 \x01(\x05\x12\r\n\x05patch\x18\x03 \x01(\x05\x12\x0e\n\x06suffix\x18\x04 \x01(\t\"\xba\x01\n\x14\x43odeGeneratorRequest\x12\x18\n\x10\x66ile_to_generate\x18\x01 \x03(\t\x12\x11\n\tparameter\x18\x02 \x01(\t\x12\x38\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\x12;\n\x10\x63ompiler_version\x18\x03 \x01(\x0b\x32!.google.protobuf.compiler.Version\"\xc1\x02\n\x15\x43odeGeneratorResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\x12\x1a\n\x12supported_features\x18\x02 \x01(\x04\x12\x42\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.File\x1a\x7f\n\x04\x46ile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x0finsertion_point\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x0f \x01(\t\x12?\n\x13generated_code_info\x18\x10 \x01(\x0b\x32\".google.protobuf.GeneratedCodeInfo\"8\n\x07\x46\x65\x61ture\x12\x10\n\x0c\x46\x45\x41TURE_NONE\x10\x00\x12\x1b\n\x17\x46\x45\x41TURE_PROTO3_OPTIONAL\x10\x01\x42W\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtosZ)google.golang.org/protobuf/types/pluginpb')
|
| 18 |
+
|
| 19 |
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
| 20 |
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.compiler.plugin_pb2', globals())
|
| 21 |
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
| 22 |
+
|
| 23 |
+
DESCRIPTOR._options = None
|
| 24 |
+
DESCRIPTOR._serialized_options = b'\n\034com.google.protobuf.compilerB\014PluginProtosZ)google.golang.org/protobuf/types/pluginpb'
|
| 25 |
+
_VERSION._serialized_start=101
|
| 26 |
+
_VERSION._serialized_end=171
|
| 27 |
+
_CODEGENERATORREQUEST._serialized_start=174
|
| 28 |
+
_CODEGENERATORREQUEST._serialized_end=360
|
| 29 |
+
_CODEGENERATORRESPONSE._serialized_start=363
|
| 30 |
+
_CODEGENERATORRESPONSE._serialized_end=684
|
| 31 |
+
_CODEGENERATORRESPONSE_FILE._serialized_start=499
|
| 32 |
+
_CODEGENERATORRESPONSE_FILE._serialized_end=626
|
| 33 |
+
_CODEGENERATORRESPONSE_FEATURE._serialized_start=628
|
| 34 |
+
_CODEGENERATORRESPONSE_FEATURE._serialized_end=684
|
| 35 |
+
# @@protoc_insertion_point(module_scope)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/_api_implementation.cpython-38-x86_64-linux-gnu.so
ADDED
|
Binary file (5.5 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/builder.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Protocol Buffers - Google's data interchange format
|
| 2 |
+
# Copyright 2008 Google Inc. All rights reserved.
|
| 3 |
+
# https://developers.google.com/protocol-buffers/
|
| 4 |
+
#
|
| 5 |
+
# Redistribution and use in source and binary forms, with or without
|
| 6 |
+
# modification, are permitted provided that the following conditions are
|
| 7 |
+
# met:
|
| 8 |
+
#
|
| 9 |
+
# * Redistributions of source code must retain the above copyright
|
| 10 |
+
# notice, this list of conditions and the following disclaimer.
|
| 11 |
+
# * Redistributions in binary form must reproduce the above
|
| 12 |
+
# copyright notice, this list of conditions and the following disclaimer
|
| 13 |
+
# in the documentation and/or other materials provided with the
|
| 14 |
+
# distribution.
|
| 15 |
+
# * Neither the name of Google Inc. nor the names of its
|
| 16 |
+
# contributors may be used to endorse or promote products derived from
|
| 17 |
+
# this software without specific prior written permission.
|
| 18 |
+
#
|
| 19 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 20 |
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 21 |
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 22 |
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 23 |
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 24 |
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 25 |
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 26 |
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 27 |
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 28 |
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 29 |
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 30 |
+
|
| 31 |
+
"""Builds descriptors, message classes and services for generated _pb2.py.
|
| 32 |
+
|
| 33 |
+
This file is only called in python generated _pb2.py files. It builds
|
| 34 |
+
descriptors, message classes and services that users can directly use
|
| 35 |
+
in generated code.
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
__author__ = 'jieluo@google.com (Jie Luo)'
|
| 39 |
+
|
| 40 |
+
from google.protobuf.internal import enum_type_wrapper
|
| 41 |
+
from google.protobuf import message as _message
|
| 42 |
+
from google.protobuf import reflection as _reflection
|
| 43 |
+
from google.protobuf import symbol_database as _symbol_database
|
| 44 |
+
|
| 45 |
+
_sym_db = _symbol_database.Default()
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def BuildMessageAndEnumDescriptors(file_des, module):
|
| 49 |
+
"""Builds message and enum descriptors.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
file_des: FileDescriptor of the .proto file
|
| 53 |
+
module: Generated _pb2 module
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
def BuildNestedDescriptors(msg_des, prefix):
|
| 57 |
+
for (name, nested_msg) in msg_des.nested_types_by_name.items():
|
| 58 |
+
module_name = prefix + name.upper()
|
| 59 |
+
module[module_name] = nested_msg
|
| 60 |
+
BuildNestedDescriptors(nested_msg, module_name + '_')
|
| 61 |
+
for enum_des in msg_des.enum_types:
|
| 62 |
+
module[prefix + enum_des.name.upper()] = enum_des
|
| 63 |
+
|
| 64 |
+
for (name, msg_des) in file_des.message_types_by_name.items():
|
| 65 |
+
module_name = '_' + name.upper()
|
| 66 |
+
module[module_name] = msg_des
|
| 67 |
+
BuildNestedDescriptors(msg_des, module_name + '_')
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def BuildTopDescriptorsAndMessages(file_des, module_name, module):
|
| 71 |
+
"""Builds top level descriptors and message classes.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
file_des: FileDescriptor of the .proto file
|
| 75 |
+
module_name: str, the name of generated _pb2 module
|
| 76 |
+
module: Generated _pb2 module
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
def BuildMessage(msg_des):
|
| 80 |
+
create_dict = {}
|
| 81 |
+
for (name, nested_msg) in msg_des.nested_types_by_name.items():
|
| 82 |
+
create_dict[name] = BuildMessage(nested_msg)
|
| 83 |
+
create_dict['DESCRIPTOR'] = msg_des
|
| 84 |
+
create_dict['__module__'] = module_name
|
| 85 |
+
message_class = _reflection.GeneratedProtocolMessageType(
|
| 86 |
+
msg_des.name, (_message.Message,), create_dict)
|
| 87 |
+
_sym_db.RegisterMessage(message_class)
|
| 88 |
+
return message_class
|
| 89 |
+
|
| 90 |
+
# top level enums
|
| 91 |
+
for (name, enum_des) in file_des.enum_types_by_name.items():
|
| 92 |
+
module['_' + name.upper()] = enum_des
|
| 93 |
+
module[name] = enum_type_wrapper.EnumTypeWrapper(enum_des)
|
| 94 |
+
for enum_value in enum_des.values:
|
| 95 |
+
module[enum_value.name] = enum_value.number
|
| 96 |
+
|
| 97 |
+
# top level extensions
|
| 98 |
+
for (name, extension_des) in file_des.extensions_by_name.items():
|
| 99 |
+
module[name.upper() + '_FIELD_NUMBER'] = extension_des.number
|
| 100 |
+
module[name] = extension_des
|
| 101 |
+
|
| 102 |
+
# services
|
| 103 |
+
for (name, service) in file_des.services_by_name.items():
|
| 104 |
+
module['_' + name.upper()] = service
|
| 105 |
+
|
| 106 |
+
# Build messages.
|
| 107 |
+
for (name, msg_des) in file_des.message_types_by_name.items():
|
| 108 |
+
module[name] = BuildMessage(msg_des)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def BuildServices(file_des, module_name, module):
|
| 112 |
+
"""Builds services classes and services stub class.
|
| 113 |
+
|
| 114 |
+
Args:
|
| 115 |
+
file_des: FileDescriptor of the .proto file
|
| 116 |
+
module_name: str, the name of generated _pb2 module
|
| 117 |
+
module: Generated _pb2 module
|
| 118 |
+
"""
|
| 119 |
+
# pylint: disable=g-import-not-at-top
|
| 120 |
+
from google.protobuf import service as _service
|
| 121 |
+
from google.protobuf import service_reflection
|
| 122 |
+
# pylint: enable=g-import-not-at-top
|
| 123 |
+
for (name, service) in file_des.services_by_name.items():
|
| 124 |
+
module[name] = service_reflection.GeneratedServiceType(
|
| 125 |
+
name, (_service.Service,),
|
| 126 |
+
dict(DESCRIPTOR=service, __module__=module_name))
|
| 127 |
+
stub_name = name + '_Stub'
|
| 128 |
+
module[stub_name] = service_reflection.GeneratedServiceStubType(
|
| 129 |
+
stub_name, (module[name],),
|
| 130 |
+
dict(DESCRIPTOR=service, __module__=module_name))
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/containers.py
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Protocol Buffers - Google's data interchange format
|
| 2 |
+
# Copyright 2008 Google Inc. All rights reserved.
|
| 3 |
+
# https://developers.google.com/protocol-buffers/
|
| 4 |
+
#
|
| 5 |
+
# Redistribution and use in source and binary forms, with or without
|
| 6 |
+
# modification, are permitted provided that the following conditions are
|
| 7 |
+
# met:
|
| 8 |
+
#
|
| 9 |
+
# * Redistributions of source code must retain the above copyright
|
| 10 |
+
# notice, this list of conditions and the following disclaimer.
|
| 11 |
+
# * Redistributions in binary form must reproduce the above
|
| 12 |
+
# copyright notice, this list of conditions and the following disclaimer
|
| 13 |
+
# in the documentation and/or other materials provided with the
|
| 14 |
+
# distribution.
|
| 15 |
+
# * Neither the name of Google Inc. nor the names of its
|
| 16 |
+
# contributors may be used to endorse or promote products derived from
|
| 17 |
+
# this software without specific prior written permission.
|
| 18 |
+
#
|
| 19 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 20 |
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 21 |
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 22 |
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 23 |
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 24 |
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 25 |
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 26 |
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 27 |
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 28 |
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 29 |
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 30 |
+
|
| 31 |
+
"""Contains container classes to represent different protocol buffer types.
|
| 32 |
+
|
| 33 |
+
This file defines container classes which represent categories of protocol
|
| 34 |
+
buffer field types which need extra maintenance. Currently these categories
|
| 35 |
+
are:
|
| 36 |
+
|
| 37 |
+
- Repeated scalar fields - These are all repeated fields which aren't
|
| 38 |
+
composite (e.g. they are of simple types like int32, string, etc).
|
| 39 |
+
- Repeated composite fields - Repeated fields which are composite. This
|
| 40 |
+
includes groups and nested messages.
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
import collections.abc
|
| 44 |
+
import copy
|
| 45 |
+
import pickle
|
| 46 |
+
from typing import (
|
| 47 |
+
Any,
|
| 48 |
+
Iterable,
|
| 49 |
+
Iterator,
|
| 50 |
+
List,
|
| 51 |
+
MutableMapping,
|
| 52 |
+
MutableSequence,
|
| 53 |
+
NoReturn,
|
| 54 |
+
Optional,
|
| 55 |
+
Sequence,
|
| 56 |
+
TypeVar,
|
| 57 |
+
Union,
|
| 58 |
+
overload,
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
_T = TypeVar('_T')
|
| 63 |
+
_K = TypeVar('_K')
|
| 64 |
+
_V = TypeVar('_V')
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class BaseContainer(Sequence[_T]):
|
| 68 |
+
"""Base container class."""
|
| 69 |
+
|
| 70 |
+
# Minimizes memory usage and disallows assignment to other attributes.
|
| 71 |
+
__slots__ = ['_message_listener', '_values']
|
| 72 |
+
|
| 73 |
+
def __init__(self, message_listener: Any) -> None:
|
| 74 |
+
"""
|
| 75 |
+
Args:
|
| 76 |
+
message_listener: A MessageListener implementation.
|
| 77 |
+
The RepeatedScalarFieldContainer will call this object's
|
| 78 |
+
Modified() method when it is modified.
|
| 79 |
+
"""
|
| 80 |
+
self._message_listener = message_listener
|
| 81 |
+
self._values = []
|
| 82 |
+
|
| 83 |
+
@overload
|
| 84 |
+
def __getitem__(self, key: int) -> _T:
|
| 85 |
+
...
|
| 86 |
+
|
| 87 |
+
@overload
|
| 88 |
+
def __getitem__(self, key: slice) -> List[_T]:
|
| 89 |
+
...
|
| 90 |
+
|
| 91 |
+
def __getitem__(self, key):
|
| 92 |
+
"""Retrieves item by the specified key."""
|
| 93 |
+
return self._values[key]
|
| 94 |
+
|
| 95 |
+
def __len__(self) -> int:
|
| 96 |
+
"""Returns the number of elements in the container."""
|
| 97 |
+
return len(self._values)
|
| 98 |
+
|
| 99 |
+
def __ne__(self, other: Any) -> bool:
|
| 100 |
+
"""Checks if another instance isn't equal to this one."""
|
| 101 |
+
# The concrete classes should define __eq__.
|
| 102 |
+
return not self == other
|
| 103 |
+
|
| 104 |
+
__hash__ = None
|
| 105 |
+
|
| 106 |
+
def __repr__(self) -> str:
|
| 107 |
+
return repr(self._values)
|
| 108 |
+
|
| 109 |
+
def sort(self, *args, **kwargs) -> None:
|
| 110 |
+
# Continue to support the old sort_function keyword argument.
|
| 111 |
+
# This is expected to be a rare occurrence, so use LBYL to avoid
|
| 112 |
+
# the overhead of actually catching KeyError.
|
| 113 |
+
if 'sort_function' in kwargs:
|
| 114 |
+
kwargs['cmp'] = kwargs.pop('sort_function')
|
| 115 |
+
self._values.sort(*args, **kwargs)
|
| 116 |
+
|
| 117 |
+
def reverse(self) -> None:
|
| 118 |
+
self._values.reverse()
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
# TODO(slebedev): Remove this. BaseContainer does *not* conform to
|
| 122 |
+
# MutableSequence, only its subclasses do.
|
| 123 |
+
collections.abc.MutableSequence.register(BaseContainer)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class RepeatedScalarFieldContainer(BaseContainer[_T], MutableSequence[_T]):
|
| 127 |
+
"""Simple, type-checked, list-like container for holding repeated scalars."""
|
| 128 |
+
|
| 129 |
+
# Disallows assignment to other attributes.
|
| 130 |
+
__slots__ = ['_type_checker']
|
| 131 |
+
|
| 132 |
+
def __init__(
|
| 133 |
+
self,
|
| 134 |
+
message_listener: Any,
|
| 135 |
+
type_checker: Any,
|
| 136 |
+
) -> None:
|
| 137 |
+
"""Args:
|
| 138 |
+
|
| 139 |
+
message_listener: A MessageListener implementation. The
|
| 140 |
+
RepeatedScalarFieldContainer will call this object's Modified() method
|
| 141 |
+
when it is modified.
|
| 142 |
+
type_checker: A type_checkers.ValueChecker instance to run on elements
|
| 143 |
+
inserted into this container.
|
| 144 |
+
"""
|
| 145 |
+
super().__init__(message_listener)
|
| 146 |
+
self._type_checker = type_checker
|
| 147 |
+
|
| 148 |
+
def append(self, value: _T) -> None:
|
| 149 |
+
"""Appends an item to the list. Similar to list.append()."""
|
| 150 |
+
self._values.append(self._type_checker.CheckValue(value))
|
| 151 |
+
if not self._message_listener.dirty:
|
| 152 |
+
self._message_listener.Modified()
|
| 153 |
+
|
| 154 |
+
def insert(self, key: int, value: _T) -> None:
|
| 155 |
+
"""Inserts the item at the specified position. Similar to list.insert()."""
|
| 156 |
+
self._values.insert(key, self._type_checker.CheckValue(value))
|
| 157 |
+
if not self._message_listener.dirty:
|
| 158 |
+
self._message_listener.Modified()
|
| 159 |
+
|
| 160 |
+
def extend(self, elem_seq: Iterable[_T]) -> None:
|
| 161 |
+
"""Extends by appending the given iterable. Similar to list.extend()."""
|
| 162 |
+
if elem_seq is None:
|
| 163 |
+
return
|
| 164 |
+
try:
|
| 165 |
+
elem_seq_iter = iter(elem_seq)
|
| 166 |
+
except TypeError:
|
| 167 |
+
if not elem_seq:
|
| 168 |
+
# silently ignore falsy inputs :-/.
|
| 169 |
+
# TODO(ptucker): Deprecate this behavior. b/18413862
|
| 170 |
+
return
|
| 171 |
+
raise
|
| 172 |
+
|
| 173 |
+
new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
|
| 174 |
+
if new_values:
|
| 175 |
+
self._values.extend(new_values)
|
| 176 |
+
self._message_listener.Modified()
|
| 177 |
+
|
| 178 |
+
def MergeFrom(
|
| 179 |
+
self,
|
| 180 |
+
other: Union['RepeatedScalarFieldContainer[_T]', Iterable[_T]],
|
| 181 |
+
) -> None:
|
| 182 |
+
"""Appends the contents of another repeated field of the same type to this
|
| 183 |
+
one. We do not check the types of the individual fields.
|
| 184 |
+
"""
|
| 185 |
+
self._values.extend(other)
|
| 186 |
+
self._message_listener.Modified()
|
| 187 |
+
|
| 188 |
+
def remove(self, elem: _T):
|
| 189 |
+
"""Removes an item from the list. Similar to list.remove()."""
|
| 190 |
+
self._values.remove(elem)
|
| 191 |
+
self._message_listener.Modified()
|
| 192 |
+
|
| 193 |
+
def pop(self, key: Optional[int] = -1) -> _T:
|
| 194 |
+
"""Removes and returns an item at a given index. Similar to list.pop()."""
|
| 195 |
+
value = self._values[key]
|
| 196 |
+
self.__delitem__(key)
|
| 197 |
+
return value
|
| 198 |
+
|
| 199 |
+
@overload
|
| 200 |
+
def __setitem__(self, key: int, value: _T) -> None:
|
| 201 |
+
...
|
| 202 |
+
|
| 203 |
+
@overload
|
| 204 |
+
def __setitem__(self, key: slice, value: Iterable[_T]) -> None:
|
| 205 |
+
...
|
| 206 |
+
|
| 207 |
+
def __setitem__(self, key, value) -> None:
|
| 208 |
+
"""Sets the item on the specified position."""
|
| 209 |
+
if isinstance(key, slice):
|
| 210 |
+
if key.step is not None:
|
| 211 |
+
raise ValueError('Extended slices not supported')
|
| 212 |
+
self._values[key] = map(self._type_checker.CheckValue, value)
|
| 213 |
+
self._message_listener.Modified()
|
| 214 |
+
else:
|
| 215 |
+
self._values[key] = self._type_checker.CheckValue(value)
|
| 216 |
+
self._message_listener.Modified()
|
| 217 |
+
|
| 218 |
+
def __delitem__(self, key: Union[int, slice]) -> None:
|
| 219 |
+
"""Deletes the item at the specified position."""
|
| 220 |
+
del self._values[key]
|
| 221 |
+
self._message_listener.Modified()
|
| 222 |
+
|
| 223 |
+
def __eq__(self, other: Any) -> bool:
|
| 224 |
+
"""Compares the current instance with another one."""
|
| 225 |
+
if self is other:
|
| 226 |
+
return True
|
| 227 |
+
# Special case for the same type which should be common and fast.
|
| 228 |
+
if isinstance(other, self.__class__):
|
| 229 |
+
return other._values == self._values
|
| 230 |
+
# We are presumably comparing against some other sequence type.
|
| 231 |
+
return other == self._values
|
| 232 |
+
|
| 233 |
+
def __deepcopy__(
|
| 234 |
+
self,
|
| 235 |
+
unused_memo: Any = None,
|
| 236 |
+
) -> 'RepeatedScalarFieldContainer[_T]':
|
| 237 |
+
clone = RepeatedScalarFieldContainer(
|
| 238 |
+
copy.deepcopy(self._message_listener), self._type_checker)
|
| 239 |
+
clone.MergeFrom(self)
|
| 240 |
+
return clone
|
| 241 |
+
|
| 242 |
+
def __reduce__(self, **kwargs) -> NoReturn:
|
| 243 |
+
raise pickle.PickleError(
|
| 244 |
+
"Can't pickle repeated scalar fields, convert to list first")
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
# TODO(slebedev): Constrain T to be a subtype of Message.
|
| 248 |
+
class RepeatedCompositeFieldContainer(BaseContainer[_T], MutableSequence[_T]):
|
| 249 |
+
"""Simple, list-like container for holding repeated composite fields."""
|
| 250 |
+
|
| 251 |
+
# Disallows assignment to other attributes.
|
| 252 |
+
__slots__ = ['_message_descriptor']
|
| 253 |
+
|
| 254 |
+
def __init__(self, message_listener: Any, message_descriptor: Any) -> None:
|
| 255 |
+
"""
|
| 256 |
+
Note that we pass in a descriptor instead of the generated directly,
|
| 257 |
+
since at the time we construct a _RepeatedCompositeFieldContainer we
|
| 258 |
+
haven't yet necessarily initialized the type that will be contained in the
|
| 259 |
+
container.
|
| 260 |
+
|
| 261 |
+
Args:
|
| 262 |
+
message_listener: A MessageListener implementation.
|
| 263 |
+
The RepeatedCompositeFieldContainer will call this object's
|
| 264 |
+
Modified() method when it is modified.
|
| 265 |
+
message_descriptor: A Descriptor instance describing the protocol type
|
| 266 |
+
that should be present in this container. We'll use the
|
| 267 |
+
_concrete_class field of this descriptor when the client calls add().
|
| 268 |
+
"""
|
| 269 |
+
super().__init__(message_listener)
|
| 270 |
+
self._message_descriptor = message_descriptor
|
| 271 |
+
|
| 272 |
+
def add(self, **kwargs: Any) -> _T:
|
| 273 |
+
"""Adds a new element at the end of the list and returns it. Keyword
|
| 274 |
+
arguments may be used to initialize the element.
|
| 275 |
+
"""
|
| 276 |
+
new_element = self._message_descriptor._concrete_class(**kwargs)
|
| 277 |
+
new_element._SetListener(self._message_listener)
|
| 278 |
+
self._values.append(new_element)
|
| 279 |
+
if not self._message_listener.dirty:
|
| 280 |
+
self._message_listener.Modified()
|
| 281 |
+
return new_element
|
| 282 |
+
|
| 283 |
+
def append(self, value: _T) -> None:
|
| 284 |
+
"""Appends one element by copying the message."""
|
| 285 |
+
new_element = self._message_descriptor._concrete_class()
|
| 286 |
+
new_element._SetListener(self._message_listener)
|
| 287 |
+
new_element.CopyFrom(value)
|
| 288 |
+
self._values.append(new_element)
|
| 289 |
+
if not self._message_listener.dirty:
|
| 290 |
+
self._message_listener.Modified()
|
| 291 |
+
|
| 292 |
+
def insert(self, key: int, value: _T) -> None:
|
| 293 |
+
"""Inserts the item at the specified position by copying."""
|
| 294 |
+
new_element = self._message_descriptor._concrete_class()
|
| 295 |
+
new_element._SetListener(self._message_listener)
|
| 296 |
+
new_element.CopyFrom(value)
|
| 297 |
+
self._values.insert(key, new_element)
|
| 298 |
+
if not self._message_listener.dirty:
|
| 299 |
+
self._message_listener.Modified()
|
| 300 |
+
|
| 301 |
+
def extend(self, elem_seq: Iterable[_T]) -> None:
|
| 302 |
+
"""Extends by appending the given sequence of elements of the same type
|
| 303 |
+
|
| 304 |
+
as this one, copying each individual message.
|
| 305 |
+
"""
|
| 306 |
+
message_class = self._message_descriptor._concrete_class
|
| 307 |
+
listener = self._message_listener
|
| 308 |
+
values = self._values
|
| 309 |
+
for message in elem_seq:
|
| 310 |
+
new_element = message_class()
|
| 311 |
+
new_element._SetListener(listener)
|
| 312 |
+
new_element.MergeFrom(message)
|
| 313 |
+
values.append(new_element)
|
| 314 |
+
listener.Modified()
|
| 315 |
+
|
| 316 |
+
def MergeFrom(
|
| 317 |
+
self,
|
| 318 |
+
other: Union['RepeatedCompositeFieldContainer[_T]', Iterable[_T]],
|
| 319 |
+
) -> None:
|
| 320 |
+
"""Appends the contents of another repeated field of the same type to this
|
| 321 |
+
one, copying each individual message.
|
| 322 |
+
"""
|
| 323 |
+
self.extend(other)
|
| 324 |
+
|
| 325 |
+
def remove(self, elem: _T) -> None:
|
| 326 |
+
"""Removes an item from the list. Similar to list.remove()."""
|
| 327 |
+
self._values.remove(elem)
|
| 328 |
+
self._message_listener.Modified()
|
| 329 |
+
|
| 330 |
+
def pop(self, key: Optional[int] = -1) -> _T:
|
| 331 |
+
"""Removes and returns an item at a given index. Similar to list.pop()."""
|
| 332 |
+
value = self._values[key]
|
| 333 |
+
self.__delitem__(key)
|
| 334 |
+
return value
|
| 335 |
+
|
| 336 |
+
@overload
|
| 337 |
+
def __setitem__(self, key: int, value: _T) -> None:
|
| 338 |
+
...
|
| 339 |
+
|
| 340 |
+
@overload
|
| 341 |
+
def __setitem__(self, key: slice, value: Iterable[_T]) -> None:
|
| 342 |
+
...
|
| 343 |
+
|
| 344 |
+
def __setitem__(self, key, value):
|
| 345 |
+
# This method is implemented to make RepeatedCompositeFieldContainer
|
| 346 |
+
# structurally compatible with typing.MutableSequence. It is
|
| 347 |
+
# otherwise unsupported and will always raise an error.
|
| 348 |
+
raise TypeError(
|
| 349 |
+
f'{self.__class__.__name__} object does not support item assignment')
|
| 350 |
+
|
| 351 |
+
def __delitem__(self, key: Union[int, slice]) -> None:
|
| 352 |
+
"""Deletes the item at the specified position."""
|
| 353 |
+
del self._values[key]
|
| 354 |
+
self._message_listener.Modified()
|
| 355 |
+
|
| 356 |
+
def __eq__(self, other: Any) -> bool:
|
| 357 |
+
"""Compares the current instance with another one."""
|
| 358 |
+
if self is other:
|
| 359 |
+
return True
|
| 360 |
+
if not isinstance(other, self.__class__):
|
| 361 |
+
raise TypeError('Can only compare repeated composite fields against '
|
| 362 |
+
'other repeated composite fields.')
|
| 363 |
+
return self._values == other._values
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
class ScalarMap(MutableMapping[_K, _V]):
|
| 367 |
+
"""Simple, type-checked, dict-like container for holding repeated scalars."""
|
| 368 |
+
|
| 369 |
+
# Disallows assignment to other attributes.
|
| 370 |
+
__slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener',
|
| 371 |
+
'_entry_descriptor']
|
| 372 |
+
|
| 373 |
+
def __init__(
|
| 374 |
+
self,
|
| 375 |
+
message_listener: Any,
|
| 376 |
+
key_checker: Any,
|
| 377 |
+
value_checker: Any,
|
| 378 |
+
entry_descriptor: Any,
|
| 379 |
+
) -> None:
|
| 380 |
+
"""
|
| 381 |
+
Args:
|
| 382 |
+
message_listener: A MessageListener implementation.
|
| 383 |
+
The ScalarMap will call this object's Modified() method when it
|
| 384 |
+
is modified.
|
| 385 |
+
key_checker: A type_checkers.ValueChecker instance to run on keys
|
| 386 |
+
inserted into this container.
|
| 387 |
+
value_checker: A type_checkers.ValueChecker instance to run on values
|
| 388 |
+
inserted into this container.
|
| 389 |
+
entry_descriptor: The MessageDescriptor of a map entry: key and value.
|
| 390 |
+
"""
|
| 391 |
+
self._message_listener = message_listener
|
| 392 |
+
self._key_checker = key_checker
|
| 393 |
+
self._value_checker = value_checker
|
| 394 |
+
self._entry_descriptor = entry_descriptor
|
| 395 |
+
self._values = {}
|
| 396 |
+
|
| 397 |
+
def __getitem__(self, key: _K) -> _V:
|
| 398 |
+
try:
|
| 399 |
+
return self._values[key]
|
| 400 |
+
except KeyError:
|
| 401 |
+
key = self._key_checker.CheckValue(key)
|
| 402 |
+
val = self._value_checker.DefaultValue()
|
| 403 |
+
self._values[key] = val
|
| 404 |
+
return val
|
| 405 |
+
|
| 406 |
+
def __contains__(self, item: _K) -> bool:
|
| 407 |
+
# We check the key's type to match the strong-typing flavor of the API.
|
| 408 |
+
# Also this makes it easier to match the behavior of the C++ implementation.
|
| 409 |
+
self._key_checker.CheckValue(item)
|
| 410 |
+
return item in self._values
|
| 411 |
+
|
| 412 |
+
@overload
|
| 413 |
+
def get(self, key: _K) -> Optional[_V]:
|
| 414 |
+
...
|
| 415 |
+
|
| 416 |
+
@overload
|
| 417 |
+
def get(self, key: _K, default: _T) -> Union[_V, _T]:
|
| 418 |
+
...
|
| 419 |
+
|
| 420 |
+
# We need to override this explicitly, because our defaultdict-like behavior
|
| 421 |
+
# will make the default implementation (from our base class) always insert
|
| 422 |
+
# the key.
|
| 423 |
+
def get(self, key, default=None):
|
| 424 |
+
if key in self:
|
| 425 |
+
return self[key]
|
| 426 |
+
else:
|
| 427 |
+
return default
|
| 428 |
+
|
| 429 |
+
def __setitem__(self, key: _K, value: _V) -> _T:
|
| 430 |
+
checked_key = self._key_checker.CheckValue(key)
|
| 431 |
+
checked_value = self._value_checker.CheckValue(value)
|
| 432 |
+
self._values[checked_key] = checked_value
|
| 433 |
+
self._message_listener.Modified()
|
| 434 |
+
|
| 435 |
+
def __delitem__(self, key: _K) -> None:
|
| 436 |
+
del self._values[key]
|
| 437 |
+
self._message_listener.Modified()
|
| 438 |
+
|
| 439 |
+
def __len__(self) -> int:
|
| 440 |
+
return len(self._values)
|
| 441 |
+
|
| 442 |
+
def __iter__(self) -> Iterator[_K]:
|
| 443 |
+
return iter(self._values)
|
| 444 |
+
|
| 445 |
+
def __repr__(self) -> str:
|
| 446 |
+
return repr(self._values)
|
| 447 |
+
|
| 448 |
+
def MergeFrom(self, other: 'ScalarMap[_K, _V]') -> None:
|
| 449 |
+
self._values.update(other._values)
|
| 450 |
+
self._message_listener.Modified()
|
| 451 |
+
|
| 452 |
+
def InvalidateIterators(self) -> None:
|
| 453 |
+
# It appears that the only way to reliably invalidate iterators to
|
| 454 |
+
# self._values is to ensure that its size changes.
|
| 455 |
+
original = self._values
|
| 456 |
+
self._values = original.copy()
|
| 457 |
+
original[None] = None
|
| 458 |
+
|
| 459 |
+
# This is defined in the abstract base, but we can do it much more cheaply.
|
| 460 |
+
def clear(self) -> None:
|
| 461 |
+
self._values.clear()
|
| 462 |
+
self._message_listener.Modified()
|
| 463 |
+
|
| 464 |
+
def GetEntryClass(self) -> Any:
|
| 465 |
+
return self._entry_descriptor._concrete_class
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
class MessageMap(MutableMapping[_K, _V]):
|
| 469 |
+
"""Simple, type-checked, dict-like container for with submessage values."""
|
| 470 |
+
|
| 471 |
+
# Disallows assignment to other attributes.
|
| 472 |
+
__slots__ = ['_key_checker', '_values', '_message_listener',
|
| 473 |
+
'_message_descriptor', '_entry_descriptor']
|
| 474 |
+
|
| 475 |
+
def __init__(
|
| 476 |
+
self,
|
| 477 |
+
message_listener: Any,
|
| 478 |
+
message_descriptor: Any,
|
| 479 |
+
key_checker: Any,
|
| 480 |
+
entry_descriptor: Any,
|
| 481 |
+
) -> None:
|
| 482 |
+
"""
|
| 483 |
+
Args:
|
| 484 |
+
message_listener: A MessageListener implementation.
|
| 485 |
+
The ScalarMap will call this object's Modified() method when it
|
| 486 |
+
is modified.
|
| 487 |
+
key_checker: A type_checkers.ValueChecker instance to run on keys
|
| 488 |
+
inserted into this container.
|
| 489 |
+
value_checker: A type_checkers.ValueChecker instance to run on values
|
| 490 |
+
inserted into this container.
|
| 491 |
+
entry_descriptor: The MessageDescriptor of a map entry: key and value.
|
| 492 |
+
"""
|
| 493 |
+
self._message_listener = message_listener
|
| 494 |
+
self._message_descriptor = message_descriptor
|
| 495 |
+
self._key_checker = key_checker
|
| 496 |
+
self._entry_descriptor = entry_descriptor
|
| 497 |
+
self._values = {}
|
| 498 |
+
|
| 499 |
+
def __getitem__(self, key: _K) -> _V:
|
| 500 |
+
key = self._key_checker.CheckValue(key)
|
| 501 |
+
try:
|
| 502 |
+
return self._values[key]
|
| 503 |
+
except KeyError:
|
| 504 |
+
new_element = self._message_descriptor._concrete_class()
|
| 505 |
+
new_element._SetListener(self._message_listener)
|
| 506 |
+
self._values[key] = new_element
|
| 507 |
+
self._message_listener.Modified()
|
| 508 |
+
return new_element
|
| 509 |
+
|
| 510 |
+
def get_or_create(self, key: _K) -> _V:
|
| 511 |
+
"""get_or_create() is an alias for getitem (ie. map[key]).
|
| 512 |
+
|
| 513 |
+
Args:
|
| 514 |
+
key: The key to get or create in the map.
|
| 515 |
+
|
| 516 |
+
This is useful in cases where you want to be explicit that the call is
|
| 517 |
+
mutating the map. This can avoid lint errors for statements like this
|
| 518 |
+
that otherwise would appear to be pointless statements:
|
| 519 |
+
|
| 520 |
+
msg.my_map[key]
|
| 521 |
+
"""
|
| 522 |
+
return self[key]
|
| 523 |
+
|
| 524 |
+
@overload
|
| 525 |
+
def get(self, key: _K) -> Optional[_V]:
|
| 526 |
+
...
|
| 527 |
+
|
| 528 |
+
@overload
|
| 529 |
+
def get(self, key: _K, default: _T) -> Union[_V, _T]:
|
| 530 |
+
...
|
| 531 |
+
|
| 532 |
+
# We need to override this explicitly, because our defaultdict-like behavior
|
| 533 |
+
# will make the default implementation (from our base class) always insert
|
| 534 |
+
# the key.
|
| 535 |
+
def get(self, key, default=None):
|
| 536 |
+
if key in self:
|
| 537 |
+
return self[key]
|
| 538 |
+
else:
|
| 539 |
+
return default
|
| 540 |
+
|
| 541 |
+
def __contains__(self, item: _K) -> bool:
|
| 542 |
+
item = self._key_checker.CheckValue(item)
|
| 543 |
+
return item in self._values
|
| 544 |
+
|
| 545 |
+
def __setitem__(self, key: _K, value: _V) -> NoReturn:
|
| 546 |
+
raise ValueError('May not set values directly, call my_map[key].foo = 5')
|
| 547 |
+
|
| 548 |
+
def __delitem__(self, key: _K) -> None:
|
| 549 |
+
key = self._key_checker.CheckValue(key)
|
| 550 |
+
del self._values[key]
|
| 551 |
+
self._message_listener.Modified()
|
| 552 |
+
|
| 553 |
+
def __len__(self) -> int:
|
| 554 |
+
return len(self._values)
|
| 555 |
+
|
| 556 |
+
def __iter__(self) -> Iterator[_K]:
|
| 557 |
+
return iter(self._values)
|
| 558 |
+
|
| 559 |
+
def __repr__(self) -> str:
|
| 560 |
+
return repr(self._values)
|
| 561 |
+
|
| 562 |
+
def MergeFrom(self, other: 'MessageMap[_K, _V]') -> None:
|
| 563 |
+
# pylint: disable=protected-access
|
| 564 |
+
for key in other._values:
|
| 565 |
+
# According to documentation: "When parsing from the wire or when merging,
|
| 566 |
+
# if there are duplicate map keys the last key seen is used".
|
| 567 |
+
if key in self:
|
| 568 |
+
del self[key]
|
| 569 |
+
self[key].CopyFrom(other[key])
|
| 570 |
+
# self._message_listener.Modified() not required here, because
|
| 571 |
+
# mutations to submessages already propagate.
|
| 572 |
+
|
| 573 |
+
def InvalidateIterators(self) -> None:
|
| 574 |
+
# It appears that the only way to reliably invalidate iterators to
|
| 575 |
+
# self._values is to ensure that its size changes.
|
| 576 |
+
original = self._values
|
| 577 |
+
self._values = original.copy()
|
| 578 |
+
original[None] = None
|
| 579 |
+
|
| 580 |
+
# This is defined in the abstract base, but we can do it much more cheaply.
|
| 581 |
+
def clear(self) -> None:
|
| 582 |
+
self._values.clear()
|
| 583 |
+
self._message_listener.Modified()
|
| 584 |
+
|
| 585 |
+
def GetEntryClass(self) -> Any:
|
| 586 |
+
return self._entry_descriptor._concrete_class
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
class _UnknownField:
|
| 590 |
+
"""A parsed unknown field."""
|
| 591 |
+
|
| 592 |
+
# Disallows assignment to other attributes.
|
| 593 |
+
__slots__ = ['_field_number', '_wire_type', '_data']
|
| 594 |
+
|
| 595 |
+
def __init__(self, field_number, wire_type, data):
|
| 596 |
+
self._field_number = field_number
|
| 597 |
+
self._wire_type = wire_type
|
| 598 |
+
self._data = data
|
| 599 |
+
return
|
| 600 |
+
|
| 601 |
+
def __lt__(self, other):
|
| 602 |
+
# pylint: disable=protected-access
|
| 603 |
+
return self._field_number < other._field_number
|
| 604 |
+
|
| 605 |
+
def __eq__(self, other):
|
| 606 |
+
if self is other:
|
| 607 |
+
return True
|
| 608 |
+
# pylint: disable=protected-access
|
| 609 |
+
return (self._field_number == other._field_number and
|
| 610 |
+
self._wire_type == other._wire_type and
|
| 611 |
+
self._data == other._data)
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
class UnknownFieldRef: # pylint: disable=missing-class-docstring
|
| 615 |
+
|
| 616 |
+
def __init__(self, parent, index):
|
| 617 |
+
self._parent = parent
|
| 618 |
+
self._index = index
|
| 619 |
+
|
| 620 |
+
def _check_valid(self):
|
| 621 |
+
if not self._parent:
|
| 622 |
+
raise ValueError('UnknownField does not exist. '
|
| 623 |
+
'The parent message might be cleared.')
|
| 624 |
+
if self._index >= len(self._parent):
|
| 625 |
+
raise ValueError('UnknownField does not exist. '
|
| 626 |
+
'The parent message might be cleared.')
|
| 627 |
+
|
| 628 |
+
@property
|
| 629 |
+
def field_number(self):
|
| 630 |
+
self._check_valid()
|
| 631 |
+
# pylint: disable=protected-access
|
| 632 |
+
return self._parent._internal_get(self._index)._field_number
|
| 633 |
+
|
| 634 |
+
@property
|
| 635 |
+
def wire_type(self):
|
| 636 |
+
self._check_valid()
|
| 637 |
+
# pylint: disable=protected-access
|
| 638 |
+
return self._parent._internal_get(self._index)._wire_type
|
| 639 |
+
|
| 640 |
+
@property
|
| 641 |
+
def data(self):
|
| 642 |
+
self._check_valid()
|
| 643 |
+
# pylint: disable=protected-access
|
| 644 |
+
return self._parent._internal_get(self._index)._data
|
| 645 |
+
|
| 646 |
+
|
| 647 |
+
class UnknownFieldSet:
|
| 648 |
+
"""UnknownField container"""
|
| 649 |
+
|
| 650 |
+
# Disallows assignment to other attributes.
|
| 651 |
+
__slots__ = ['_values']
|
| 652 |
+
|
| 653 |
+
def __init__(self):
|
| 654 |
+
self._values = []
|
| 655 |
+
|
| 656 |
+
def __getitem__(self, index):
|
| 657 |
+
if self._values is None:
|
| 658 |
+
raise ValueError('UnknownFields does not exist. '
|
| 659 |
+
'The parent message might be cleared.')
|
| 660 |
+
size = len(self._values)
|
| 661 |
+
if index < 0:
|
| 662 |
+
index += size
|
| 663 |
+
if index < 0 or index >= size:
|
| 664 |
+
raise IndexError('index %d out of range'.index)
|
| 665 |
+
|
| 666 |
+
return UnknownFieldRef(self, index)
|
| 667 |
+
|
| 668 |
+
def _internal_get(self, index):
|
| 669 |
+
return self._values[index]
|
| 670 |
+
|
| 671 |
+
def __len__(self):
|
| 672 |
+
if self._values is None:
|
| 673 |
+
raise ValueError('UnknownFields does not exist. '
|
| 674 |
+
'The parent message might be cleared.')
|
| 675 |
+
return len(self._values)
|
| 676 |
+
|
| 677 |
+
def _add(self, field_number, wire_type, data):
|
| 678 |
+
unknown_field = _UnknownField(field_number, wire_type, data)
|
| 679 |
+
self._values.append(unknown_field)
|
| 680 |
+
return unknown_field
|
| 681 |
+
|
| 682 |
+
def __iter__(self):
|
| 683 |
+
for i in range(len(self)):
|
| 684 |
+
yield UnknownFieldRef(self, i)
|
| 685 |
+
|
| 686 |
+
def _extend(self, other):
|
| 687 |
+
if other is None:
|
| 688 |
+
return
|
| 689 |
+
# pylint: disable=protected-access
|
| 690 |
+
self._values.extend(other._values)
|
| 691 |
+
|
| 692 |
+
def __eq__(self, other):
|
| 693 |
+
if self is other:
|
| 694 |
+
return True
|
| 695 |
+
# Sort unknown fields because their order shouldn't
|
| 696 |
+
# affect equality test.
|
| 697 |
+
values = list(self._values)
|
| 698 |
+
if other is None:
|
| 699 |
+
return not values
|
| 700 |
+
values.sort()
|
| 701 |
+
# pylint: disable=protected-access
|
| 702 |
+
other_values = sorted(other._values)
|
| 703 |
+
return values == other_values
|
| 704 |
+
|
| 705 |
+
def _clear(self):
|
| 706 |
+
for value in self._values:
|
| 707 |
+
# pylint: disable=protected-access
|
| 708 |
+
if isinstance(value._data, UnknownFieldSet):
|
| 709 |
+
value._data._clear() # pylint: disable=protected-access
|
| 710 |
+
self._values = None
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/google/protobuf/internal/type_checkers.py
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Protocol Buffers - Google's data interchange format
|
| 2 |
+
# Copyright 2008 Google Inc. All rights reserved.
|
| 3 |
+
# https://developers.google.com/protocol-buffers/
|
| 4 |
+
#
|
| 5 |
+
# Redistribution and use in source and binary forms, with or without
|
| 6 |
+
# modification, are permitted provided that the following conditions are
|
| 7 |
+
# met:
|
| 8 |
+
#
|
| 9 |
+
# * Redistributions of source code must retain the above copyright
|
| 10 |
+
# notice, this list of conditions and the following disclaimer.
|
| 11 |
+
# * Redistributions in binary form must reproduce the above
|
| 12 |
+
# copyright notice, this list of conditions and the following disclaimer
|
| 13 |
+
# in the documentation and/or other materials provided with the
|
| 14 |
+
# distribution.
|
| 15 |
+
# * Neither the name of Google Inc. nor the names of its
|
| 16 |
+
# contributors may be used to endorse or promote products derived from
|
| 17 |
+
# this software without specific prior written permission.
|
| 18 |
+
#
|
| 19 |
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 20 |
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 21 |
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 22 |
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 23 |
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 24 |
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 25 |
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 26 |
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 27 |
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 28 |
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 29 |
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 30 |
+
|
| 31 |
+
"""Provides type checking routines.
|
| 32 |
+
|
| 33 |
+
This module defines type checking utilities in the forms of dictionaries:
|
| 34 |
+
|
| 35 |
+
VALUE_CHECKERS: A dictionary of field types and a value validation object.
|
| 36 |
+
TYPE_TO_BYTE_SIZE_FN: A dictionary with field types and a size computing
|
| 37 |
+
function.
|
| 38 |
+
TYPE_TO_SERIALIZE_METHOD: A dictionary with field types and serialization
|
| 39 |
+
function.
|
| 40 |
+
FIELD_TYPE_TO_WIRE_TYPE: A dictionary with field typed and their
|
| 41 |
+
corresponding wire types.
|
| 42 |
+
TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization
|
| 43 |
+
function.
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
__author__ = 'robinson@google.com (Will Robinson)'
|
| 47 |
+
|
| 48 |
+
import ctypes
|
| 49 |
+
import numbers
|
| 50 |
+
|
| 51 |
+
from google.protobuf.internal import decoder
|
| 52 |
+
from google.protobuf.internal import encoder
|
| 53 |
+
from google.protobuf.internal import wire_format
|
| 54 |
+
from google.protobuf import descriptor
|
| 55 |
+
|
| 56 |
+
_FieldDescriptor = descriptor.FieldDescriptor
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def TruncateToFourByteFloat(original):
|
| 60 |
+
return ctypes.c_float(original).value
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def ToShortestFloat(original):
|
| 64 |
+
"""Returns the shortest float that has same value in wire."""
|
| 65 |
+
# All 4 byte floats have between 6 and 9 significant digits, so we
|
| 66 |
+
# start with 6 as the lower bound.
|
| 67 |
+
# It has to be iterative because use '.9g' directly can not get rid
|
| 68 |
+
# of the noises for most values. For example if set a float_field=0.9
|
| 69 |
+
# use '.9g' will print 0.899999976.
|
| 70 |
+
precision = 6
|
| 71 |
+
rounded = float('{0:.{1}g}'.format(original, precision))
|
| 72 |
+
while TruncateToFourByteFloat(rounded) != original:
|
| 73 |
+
precision += 1
|
| 74 |
+
rounded = float('{0:.{1}g}'.format(original, precision))
|
| 75 |
+
return rounded
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def SupportsOpenEnums(field_descriptor):
|
| 79 |
+
return field_descriptor.containing_type.syntax == 'proto3'
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def GetTypeChecker(field):
|
| 83 |
+
"""Returns a type checker for a message field of the specified types.
|
| 84 |
+
|
| 85 |
+
Args:
|
| 86 |
+
field: FieldDescriptor object for this field.
|
| 87 |
+
|
| 88 |
+
Returns:
|
| 89 |
+
An instance of TypeChecker which can be used to verify the types
|
| 90 |
+
of values assigned to a field of the specified type.
|
| 91 |
+
"""
|
| 92 |
+
if (field.cpp_type == _FieldDescriptor.CPPTYPE_STRING and
|
| 93 |
+
field.type == _FieldDescriptor.TYPE_STRING):
|
| 94 |
+
return UnicodeValueChecker()
|
| 95 |
+
if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM:
|
| 96 |
+
if SupportsOpenEnums(field):
|
| 97 |
+
# When open enums are supported, any int32 can be assigned.
|
| 98 |
+
return _VALUE_CHECKERS[_FieldDescriptor.CPPTYPE_INT32]
|
| 99 |
+
else:
|
| 100 |
+
return EnumValueChecker(field.enum_type)
|
| 101 |
+
return _VALUE_CHECKERS[field.cpp_type]
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# None of the typecheckers below make any attempt to guard against people
|
| 105 |
+
# subclassing builtin types and doing weird things. We're not trying to
|
| 106 |
+
# protect against malicious clients here, just people accidentally shooting
|
| 107 |
+
# themselves in the foot in obvious ways.
|
| 108 |
+
class TypeChecker(object):
|
| 109 |
+
|
| 110 |
+
"""Type checker used to catch type errors as early as possible
|
| 111 |
+
when the client is setting scalar fields in protocol messages.
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
def __init__(self, *acceptable_types):
|
| 115 |
+
self._acceptable_types = acceptable_types
|
| 116 |
+
|
| 117 |
+
def CheckValue(self, proposed_value):
|
| 118 |
+
"""Type check the provided value and return it.
|
| 119 |
+
|
| 120 |
+
The returned value might have been normalized to another type.
|
| 121 |
+
"""
|
| 122 |
+
if not isinstance(proposed_value, self._acceptable_types):
|
| 123 |
+
message = ('%.1024r has type %s, but expected one of: %s' %
|
| 124 |
+
(proposed_value, type(proposed_value), self._acceptable_types))
|
| 125 |
+
raise TypeError(message)
|
| 126 |
+
return proposed_value
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class TypeCheckerWithDefault(TypeChecker):
|
| 130 |
+
|
| 131 |
+
def __init__(self, default_value, *acceptable_types):
|
| 132 |
+
TypeChecker.__init__(self, *acceptable_types)
|
| 133 |
+
self._default_value = default_value
|
| 134 |
+
|
| 135 |
+
def DefaultValue(self):
|
| 136 |
+
return self._default_value
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
class BoolValueChecker(object):
|
| 140 |
+
"""Type checker used for bool fields."""
|
| 141 |
+
|
| 142 |
+
def CheckValue(self, proposed_value):
|
| 143 |
+
if not hasattr(proposed_value, '__index__') or (
|
| 144 |
+
type(proposed_value).__module__ == 'numpy' and
|
| 145 |
+
type(proposed_value).__name__ == 'ndarray'):
|
| 146 |
+
message = ('%.1024r has type %s, but expected one of: %s' %
|
| 147 |
+
(proposed_value, type(proposed_value), (bool, int)))
|
| 148 |
+
raise TypeError(message)
|
| 149 |
+
return bool(proposed_value)
|
| 150 |
+
|
| 151 |
+
def DefaultValue(self):
|
| 152 |
+
return False
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
# IntValueChecker and its subclasses perform integer type-checks
|
| 156 |
+
# and bounds-checks.
|
| 157 |
+
class IntValueChecker(object):
|
| 158 |
+
|
| 159 |
+
"""Checker used for integer fields. Performs type-check and range check."""
|
| 160 |
+
|
| 161 |
+
def CheckValue(self, proposed_value):
|
| 162 |
+
if not hasattr(proposed_value, '__index__') or (
|
| 163 |
+
type(proposed_value).__module__ == 'numpy' and
|
| 164 |
+
type(proposed_value).__name__ == 'ndarray'):
|
| 165 |
+
message = ('%.1024r has type %s, but expected one of: %s' %
|
| 166 |
+
(proposed_value, type(proposed_value), (int,)))
|
| 167 |
+
raise TypeError(message)
|
| 168 |
+
|
| 169 |
+
if not self._MIN <= int(proposed_value) <= self._MAX:
|
| 170 |
+
raise ValueError('Value out of range: %d' % proposed_value)
|
| 171 |
+
# We force all values to int to make alternate implementations where the
|
| 172 |
+
# distinction is more significant (e.g. the C++ implementation) simpler.
|
| 173 |
+
proposed_value = int(proposed_value)
|
| 174 |
+
return proposed_value
|
| 175 |
+
|
| 176 |
+
def DefaultValue(self):
|
| 177 |
+
return 0
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class EnumValueChecker(object):
|
| 181 |
+
|
| 182 |
+
"""Checker used for enum fields. Performs type-check and range check."""
|
| 183 |
+
|
| 184 |
+
def __init__(self, enum_type):
|
| 185 |
+
self._enum_type = enum_type
|
| 186 |
+
|
| 187 |
+
def CheckValue(self, proposed_value):
|
| 188 |
+
if not isinstance(proposed_value, numbers.Integral):
|
| 189 |
+
message = ('%.1024r has type %s, but expected one of: %s' %
|
| 190 |
+
(proposed_value, type(proposed_value), (int,)))
|
| 191 |
+
raise TypeError(message)
|
| 192 |
+
if int(proposed_value) not in self._enum_type.values_by_number:
|
| 193 |
+
raise ValueError('Unknown enum value: %d' % proposed_value)
|
| 194 |
+
return proposed_value
|
| 195 |
+
|
| 196 |
+
def DefaultValue(self):
|
| 197 |
+
return self._enum_type.values[0].number
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class UnicodeValueChecker(object):
|
| 201 |
+
|
| 202 |
+
"""Checker used for string fields.
|
| 203 |
+
|
| 204 |
+
Always returns a unicode value, even if the input is of type str.
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
def CheckValue(self, proposed_value):
|
| 208 |
+
if not isinstance(proposed_value, (bytes, str)):
|
| 209 |
+
message = ('%.1024r has type %s, but expected one of: %s' %
|
| 210 |
+
(proposed_value, type(proposed_value), (bytes, str)))
|
| 211 |
+
raise TypeError(message)
|
| 212 |
+
|
| 213 |
+
# If the value is of type 'bytes' make sure that it is valid UTF-8 data.
|
| 214 |
+
if isinstance(proposed_value, bytes):
|
| 215 |
+
try:
|
| 216 |
+
proposed_value = proposed_value.decode('utf-8')
|
| 217 |
+
except UnicodeDecodeError:
|
| 218 |
+
raise ValueError('%.1024r has type bytes, but isn\'t valid UTF-8 '
|
| 219 |
+
'encoding. Non-UTF-8 strings must be converted to '
|
| 220 |
+
'unicode objects before being added.' %
|
| 221 |
+
(proposed_value))
|
| 222 |
+
else:
|
| 223 |
+
try:
|
| 224 |
+
proposed_value.encode('utf8')
|
| 225 |
+
except UnicodeEncodeError:
|
| 226 |
+
raise ValueError('%.1024r isn\'t a valid unicode string and '
|
| 227 |
+
'can\'t be encoded in UTF-8.'%
|
| 228 |
+
(proposed_value))
|
| 229 |
+
|
| 230 |
+
return proposed_value
|
| 231 |
+
|
| 232 |
+
def DefaultValue(self):
|
| 233 |
+
return u""
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
class Int32ValueChecker(IntValueChecker):
|
| 237 |
+
# We're sure to use ints instead of longs here since comparison may be more
|
| 238 |
+
# efficient.
|
| 239 |
+
_MIN = -2147483648
|
| 240 |
+
_MAX = 2147483647
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class Uint32ValueChecker(IntValueChecker):
|
| 244 |
+
_MIN = 0
|
| 245 |
+
_MAX = (1 << 32) - 1
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
class Int64ValueChecker(IntValueChecker):
|
| 249 |
+
_MIN = -(1 << 63)
|
| 250 |
+
_MAX = (1 << 63) - 1
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class Uint64ValueChecker(IntValueChecker):
|
| 254 |
+
_MIN = 0
|
| 255 |
+
_MAX = (1 << 64) - 1
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
# The max 4 bytes float is about 3.4028234663852886e+38
|
| 259 |
+
_FLOAT_MAX = float.fromhex('0x1.fffffep+127')
|
| 260 |
+
_FLOAT_MIN = -_FLOAT_MAX
|
| 261 |
+
_INF = float('inf')
|
| 262 |
+
_NEG_INF = float('-inf')
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
class DoubleValueChecker(object):
|
| 266 |
+
"""Checker used for double fields.
|
| 267 |
+
|
| 268 |
+
Performs type-check and range check.
|
| 269 |
+
"""
|
| 270 |
+
|
| 271 |
+
def CheckValue(self, proposed_value):
|
| 272 |
+
"""Check and convert proposed_value to float."""
|
| 273 |
+
if (not hasattr(proposed_value, '__float__') and
|
| 274 |
+
not hasattr(proposed_value, '__index__')) or (
|
| 275 |
+
type(proposed_value).__module__ == 'numpy' and
|
| 276 |
+
type(proposed_value).__name__ == 'ndarray'):
|
| 277 |
+
message = ('%.1024r has type %s, but expected one of: int, float' %
|
| 278 |
+
(proposed_value, type(proposed_value)))
|
| 279 |
+
raise TypeError(message)
|
| 280 |
+
return float(proposed_value)
|
| 281 |
+
|
| 282 |
+
def DefaultValue(self):
|
| 283 |
+
return 0.0
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
class FloatValueChecker(DoubleValueChecker):
|
| 287 |
+
"""Checker used for float fields.
|
| 288 |
+
|
| 289 |
+
Performs type-check and range check.
|
| 290 |
+
|
| 291 |
+
Values exceeding a 32-bit float will be converted to inf/-inf.
|
| 292 |
+
"""
|
| 293 |
+
|
| 294 |
+
def CheckValue(self, proposed_value):
|
| 295 |
+
"""Check and convert proposed_value to float."""
|
| 296 |
+
converted_value = super().CheckValue(proposed_value)
|
| 297 |
+
# This inf rounding matches the C++ proto SafeDoubleToFloat logic.
|
| 298 |
+
if converted_value > _FLOAT_MAX:
|
| 299 |
+
return _INF
|
| 300 |
+
if converted_value < _FLOAT_MIN:
|
| 301 |
+
return _NEG_INF
|
| 302 |
+
|
| 303 |
+
return TruncateToFourByteFloat(converted_value)
|
| 304 |
+
|
| 305 |
+
# Type-checkers for all scalar CPPTYPEs.
|
| 306 |
+
_VALUE_CHECKERS = {
|
| 307 |
+
_FieldDescriptor.CPPTYPE_INT32: Int32ValueChecker(),
|
| 308 |
+
_FieldDescriptor.CPPTYPE_INT64: Int64ValueChecker(),
|
| 309 |
+
_FieldDescriptor.CPPTYPE_UINT32: Uint32ValueChecker(),
|
| 310 |
+
_FieldDescriptor.CPPTYPE_UINT64: Uint64ValueChecker(),
|
| 311 |
+
_FieldDescriptor.CPPTYPE_DOUBLE: DoubleValueChecker(),
|
| 312 |
+
_FieldDescriptor.CPPTYPE_FLOAT: FloatValueChecker(),
|
| 313 |
+
_FieldDescriptor.CPPTYPE_BOOL: BoolValueChecker(),
|
| 314 |
+
_FieldDescriptor.CPPTYPE_STRING: TypeCheckerWithDefault(b'', bytes),
|
| 315 |
+
}
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
# Map from field type to a function F, such that F(field_num, value)
|
| 319 |
+
# gives the total byte size for a value of the given type. This
|
| 320 |
+
# byte size includes tag information and any other additional space
|
| 321 |
+
# associated with serializing "value".
|
| 322 |
+
TYPE_TO_BYTE_SIZE_FN = {
|
| 323 |
+
_FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize,
|
| 324 |
+
_FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize,
|
| 325 |
+
_FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize,
|
| 326 |
+
_FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize,
|
| 327 |
+
_FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize,
|
| 328 |
+
_FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize,
|
| 329 |
+
_FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize,
|
| 330 |
+
_FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize,
|
| 331 |
+
_FieldDescriptor.TYPE_STRING: wire_format.StringByteSize,
|
| 332 |
+
_FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize,
|
| 333 |
+
_FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize,
|
| 334 |
+
_FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize,
|
| 335 |
+
_FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize,
|
| 336 |
+
_FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize,
|
| 337 |
+
_FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize,
|
| 338 |
+
_FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize,
|
| 339 |
+
_FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize,
|
| 340 |
+
_FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
# Maps from field types to encoder constructors.
|
| 345 |
+
TYPE_TO_ENCODER = {
|
| 346 |
+
_FieldDescriptor.TYPE_DOUBLE: encoder.DoubleEncoder,
|
| 347 |
+
_FieldDescriptor.TYPE_FLOAT: encoder.FloatEncoder,
|
| 348 |
+
_FieldDescriptor.TYPE_INT64: encoder.Int64Encoder,
|
| 349 |
+
_FieldDescriptor.TYPE_UINT64: encoder.UInt64Encoder,
|
| 350 |
+
_FieldDescriptor.TYPE_INT32: encoder.Int32Encoder,
|
| 351 |
+
_FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Encoder,
|
| 352 |
+
_FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Encoder,
|
| 353 |
+
_FieldDescriptor.TYPE_BOOL: encoder.BoolEncoder,
|
| 354 |
+
_FieldDescriptor.TYPE_STRING: encoder.StringEncoder,
|
| 355 |
+
_FieldDescriptor.TYPE_GROUP: encoder.GroupEncoder,
|
| 356 |
+
_FieldDescriptor.TYPE_MESSAGE: encoder.MessageEncoder,
|
| 357 |
+
_FieldDescriptor.TYPE_BYTES: encoder.BytesEncoder,
|
| 358 |
+
_FieldDescriptor.TYPE_UINT32: encoder.UInt32Encoder,
|
| 359 |
+
_FieldDescriptor.TYPE_ENUM: encoder.EnumEncoder,
|
| 360 |
+
_FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Encoder,
|
| 361 |
+
_FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Encoder,
|
| 362 |
+
_FieldDescriptor.TYPE_SINT32: encoder.SInt32Encoder,
|
| 363 |
+
_FieldDescriptor.TYPE_SINT64: encoder.SInt64Encoder,
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
# Maps from field types to sizer constructors.
|
| 368 |
+
TYPE_TO_SIZER = {
|
| 369 |
+
_FieldDescriptor.TYPE_DOUBLE: encoder.DoubleSizer,
|
| 370 |
+
_FieldDescriptor.TYPE_FLOAT: encoder.FloatSizer,
|
| 371 |
+
_FieldDescriptor.TYPE_INT64: encoder.Int64Sizer,
|
| 372 |
+
_FieldDescriptor.TYPE_UINT64: encoder.UInt64Sizer,
|
| 373 |
+
_FieldDescriptor.TYPE_INT32: encoder.Int32Sizer,
|
| 374 |
+
_FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Sizer,
|
| 375 |
+
_FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Sizer,
|
| 376 |
+
_FieldDescriptor.TYPE_BOOL: encoder.BoolSizer,
|
| 377 |
+
_FieldDescriptor.TYPE_STRING: encoder.StringSizer,
|
| 378 |
+
_FieldDescriptor.TYPE_GROUP: encoder.GroupSizer,
|
| 379 |
+
_FieldDescriptor.TYPE_MESSAGE: encoder.MessageSizer,
|
| 380 |
+
_FieldDescriptor.TYPE_BYTES: encoder.BytesSizer,
|
| 381 |
+
_FieldDescriptor.TYPE_UINT32: encoder.UInt32Sizer,
|
| 382 |
+
_FieldDescriptor.TYPE_ENUM: encoder.EnumSizer,
|
| 383 |
+
_FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Sizer,
|
| 384 |
+
_FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Sizer,
|
| 385 |
+
_FieldDescriptor.TYPE_SINT32: encoder.SInt32Sizer,
|
| 386 |
+
_FieldDescriptor.TYPE_SINT64: encoder.SInt64Sizer,
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
# Maps from field type to a decoder constructor.
|
| 391 |
+
TYPE_TO_DECODER = {
|
| 392 |
+
_FieldDescriptor.TYPE_DOUBLE: decoder.DoubleDecoder,
|
| 393 |
+
_FieldDescriptor.TYPE_FLOAT: decoder.FloatDecoder,
|
| 394 |
+
_FieldDescriptor.TYPE_INT64: decoder.Int64Decoder,
|
| 395 |
+
_FieldDescriptor.TYPE_UINT64: decoder.UInt64Decoder,
|
| 396 |
+
_FieldDescriptor.TYPE_INT32: decoder.Int32Decoder,
|
| 397 |
+
_FieldDescriptor.TYPE_FIXED64: decoder.Fixed64Decoder,
|
| 398 |
+
_FieldDescriptor.TYPE_FIXED32: decoder.Fixed32Decoder,
|
| 399 |
+
_FieldDescriptor.TYPE_BOOL: decoder.BoolDecoder,
|
| 400 |
+
_FieldDescriptor.TYPE_STRING: decoder.StringDecoder,
|
| 401 |
+
_FieldDescriptor.TYPE_GROUP: decoder.GroupDecoder,
|
| 402 |
+
_FieldDescriptor.TYPE_MESSAGE: decoder.MessageDecoder,
|
| 403 |
+
_FieldDescriptor.TYPE_BYTES: decoder.BytesDecoder,
|
| 404 |
+
_FieldDescriptor.TYPE_UINT32: decoder.UInt32Decoder,
|
| 405 |
+
_FieldDescriptor.TYPE_ENUM: decoder.EnumDecoder,
|
| 406 |
+
_FieldDescriptor.TYPE_SFIXED32: decoder.SFixed32Decoder,
|
| 407 |
+
_FieldDescriptor.TYPE_SFIXED64: decoder.SFixed64Decoder,
|
| 408 |
+
_FieldDescriptor.TYPE_SINT32: decoder.SInt32Decoder,
|
| 409 |
+
_FieldDescriptor.TYPE_SINT64: decoder.SInt64Decoder,
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
# Maps from field type to expected wiretype.
|
| 413 |
+
FIELD_TYPE_TO_WIRE_TYPE = {
|
| 414 |
+
_FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64,
|
| 415 |
+
_FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32,
|
| 416 |
+
_FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT,
|
| 417 |
+
_FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT,
|
| 418 |
+
_FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT,
|
| 419 |
+
_FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64,
|
| 420 |
+
_FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32,
|
| 421 |
+
_FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT,
|
| 422 |
+
_FieldDescriptor.TYPE_STRING:
|
| 423 |
+
wire_format.WIRETYPE_LENGTH_DELIMITED,
|
| 424 |
+
_FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP,
|
| 425 |
+
_FieldDescriptor.TYPE_MESSAGE:
|
| 426 |
+
wire_format.WIRETYPE_LENGTH_DELIMITED,
|
| 427 |
+
_FieldDescriptor.TYPE_BYTES:
|
| 428 |
+
wire_format.WIRETYPE_LENGTH_DELIMITED,
|
| 429 |
+
_FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT,
|
| 430 |
+
_FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT,
|
| 431 |
+
_FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32,
|
| 432 |
+
_FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64,
|
| 433 |
+
_FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT,
|
| 434 |
+
_FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT,
|
| 435 |
+
}
|