Intelegentny_Pszczelarz/.venv/Lib/site-packages/google/oauth2/gdch_credentials.py
2023-06-19 00:49:18 +02:00

252 lines
8.8 KiB
Python

# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Experimental GDCH credentials support.
"""
import datetime
from google.auth import _helpers
from google.auth import _service_account_info
from google.auth import credentials
from google.auth import exceptions
from google.auth import jwt
from google.oauth2 import _client
TOKEN_EXCHANGE_TYPE = "urn:ietf:params:oauth:token-type:token-exchange"
ACCESS_TOKEN_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
SERVICE_ACCOUNT_TOKEN_TYPE = "urn:k8s:params:oauth:token-type:serviceaccount"
JWT_LIFETIME = datetime.timedelta(seconds=3600) # 1 hour
class ServiceAccountCredentials(credentials.Credentials):
"""Credentials for GDCH (`Google Distributed Cloud Hosted`_) for service
account users.
.. _Google Distributed Cloud Hosted:
https://cloud.google.com/blog/topics/hybrid-cloud/\
announcing-google-distributed-cloud-edge-and-hosted
To create a GDCH service account credential, first create a JSON file of
the following format::
{
"type": "gdch_service_account",
"format_version": "1",
"project": "<project name>",
"private_key_id": "<key id>",
"private_key": "-----BEGIN EC PRIVATE KEY-----\n<key bytes>\n-----END EC PRIVATE KEY-----\n",
"name": "<service identity name>",
"ca_cert_path": "<CA cert path>",
"token_uri": "https://service-identity.<Domain>/authenticate"
}
The "format_version" field stands for the format of the JSON file. For now
it is always "1". The `private_key_id` and `private_key` is used for signing.
The `ca_cert_path` is used for token server TLS certificate verification.
After the JSON file is created, set `GOOGLE_APPLICATION_CREDENTIALS` environment
variable to the JSON file path, then use the following code to create the
credential::
import google.auth
credential, _ = google.auth.default()
credential = credential.with_gdch_audience("<the audience>")
We can also create the credential directly::
from google.oauth import gdch_credentials
credential = gdch_credentials.ServiceAccountCredentials.from_service_account_file("<the json file path>")
credential = credential.with_gdch_audience("<the audience>")
The token is obtained in the following way. This class first creates a
self signed JWT. It uses the `name` value as the `iss` and `sub` claim, and
the `token_uri` as the `aud` claim, and signs the JWT with the `private_key`.
It then sends the JWT to the `token_uri` to exchange a final token for
`audience`.
"""
def __init__(
self, signer, service_identity_name, project, audience, token_uri, ca_cert_path
):
"""
Args:
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
service_identity_name (str): The service identity name. It will be
used as the `iss` and `sub` claim in the self signed JWT.
project (str): The project.
audience (str): The audience for the final token.
token_uri (str): The token server uri.
ca_cert_path (str): The CA cert path for token server side TLS
certificate verification. If the token server uses well known
CA, then this parameter can be `None`.
"""
super(ServiceAccountCredentials, self).__init__()
self._signer = signer
self._service_identity_name = service_identity_name
self._project = project
self._audience = audience
self._token_uri = token_uri
self._ca_cert_path = ca_cert_path
def _create_jwt(self):
now = _helpers.utcnow()
expiry = now + JWT_LIFETIME
iss_sub_value = "system:serviceaccount:{}:{}".format(
self._project, self._service_identity_name
)
payload = {
"iss": iss_sub_value,
"sub": iss_sub_value,
"aud": self._token_uri,
"iat": _helpers.datetime_to_secs(now),
"exp": _helpers.datetime_to_secs(expiry),
}
return _helpers.from_bytes(jwt.encode(self._signer, payload))
@_helpers.copy_docstring(credentials.Credentials)
def refresh(self, request):
import google.auth.transport.requests
if not isinstance(request, google.auth.transport.requests.Request):
raise exceptions.RefreshError(
"For GDCH service account credentials, request must be a google.auth.transport.requests.Request object"
)
# Create a self signed JWT, and do token exchange.
jwt_token = self._create_jwt()
request_body = {
"grant_type": TOKEN_EXCHANGE_TYPE,
"audience": self._audience,
"requested_token_type": ACCESS_TOKEN_TOKEN_TYPE,
"subject_token": jwt_token,
"subject_token_type": SERVICE_ACCOUNT_TOKEN_TYPE,
}
response_data = _client._token_endpoint_request(
request,
self._token_uri,
request_body,
access_token=None,
use_json=True,
verify=self._ca_cert_path,
)
self.token, _, self.expiry, _ = _client._handle_refresh_grant_response(
response_data, None
)
def with_gdch_audience(self, audience):
"""Create a copy of GDCH credentials with the specified audience.
Args:
audience (str): The intended audience for GDCH credentials.
"""
return self.__class__(
self._signer,
self._service_identity_name,
self._project,
audience,
self._token_uri,
self._ca_cert_path,
)
@classmethod
def _from_signer_and_info(cls, signer, info):
"""Creates a Credentials instance from a signer and service account
info.
Args:
signer (google.auth.crypt.Signer): The signer used to sign JWTs.
info (Mapping[str, str]): The service account info.
Returns:
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
credentials.
Raises:
ValueError: If the info is not in the expected format.
"""
if info["format_version"] != "1":
raise ValueError("Only format version 1 is supported")
return cls(
signer,
info["name"], # service_identity_name
info["project"],
None, # audience
info["token_uri"],
info.get("ca_cert_path", None),
)
@classmethod
def from_service_account_info(cls, info):
"""Creates a Credentials instance from parsed service account info.
Args:
info (Mapping[str, str]): The service account info in Google
format.
kwargs: Additional arguments to pass to the constructor.
Returns:
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
credentials.
Raises:
ValueError: If the info is not in the expected format.
"""
signer = _service_account_info.from_dict(
info,
require=[
"format_version",
"private_key_id",
"private_key",
"name",
"project",
"token_uri",
],
use_rsa_signer=False,
)
return cls._from_signer_and_info(signer, info)
@classmethod
def from_service_account_file(cls, filename):
"""Creates a Credentials instance from a service account json file.
Args:
filename (str): The path to the service account json file.
kwargs: Additional arguments to pass to the constructor.
Returns:
google.oauth2.gdch_credentials.ServiceAccountCredentials: The constructed
credentials.
"""
info, signer = _service_account_info.from_filename(
filename,
require=[
"format_version",
"private_key_id",
"private_key",
"name",
"project",
"token_uri",
],
use_rsa_signer=False,
)
return cls._from_signer_and_info(signer, info)