aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--google/auth/compute_engine/credentials.py21
-rw-r--r--noxfile.py1
-rw-r--r--tests/compute_engine/test_credentials.py82
4 files changed, 100 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index 88a8b8b..598752f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,8 +31,11 @@ tests/data/user-key.json
# PyCharm configuration:
.idea
+venv/
# Generated files
pylintrc
pylintrc.test
pytype_output/
+
+.python-version
diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py
index fc14fcc..e35907a 100644
--- a/google/auth/compute_engine/credentials.py
+++ b/google/auth/compute_engine/credentials.py
@@ -136,6 +136,7 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing):
token_uri=_DEFAULT_TOKEN_URI,
additional_claims=None,
service_account_email=None,
+ signer=None,
):
"""
Args:
@@ -150,6 +151,9 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing):
service_account_email (str): Optional explicit service account to
use to sign JWT tokens.
By default, this is the default GCE service account.
+ signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+ In case the signer is specified, the request argument will be
+ ignored.
"""
super(IDTokenCredentials, self).__init__()
@@ -158,11 +162,13 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing):
service_account_email = sa_info["email"]
self._service_account_email = service_account_email
- self._signer = iam.Signer(
- request=request,
- credentials=Credentials(),
- service_account_email=service_account_email,
- )
+ if signer is None:
+ signer = iam.Signer(
+ request=request,
+ credentials=Credentials(),
+ service_account_email=service_account_email,
+ )
+ self._signer = signer
self._token_uri = token_uri
self._target_audience = target_audience
@@ -182,12 +188,15 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing):
google.auth.service_account.IDTokenCredentials: A new credentials
instance.
"""
+ # since the signer is already instantiated,
+ # the request is not needed
return self.__class__(
- self._signer,
+ None,
service_account_email=self._service_account_email,
token_uri=self._token_uri,
target_audience=target_audience,
additional_claims=self._additional_claims.copy(),
+ signer=self.signer,
)
def _make_authorization_grant_assertion(self):
diff --git a/noxfile.py b/noxfile.py
index e170ee5..d75361f 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -25,6 +25,7 @@ TEST_DEPENDENCIES = [
"requests",
"urllib3",
"cryptography",
+ "responses",
"grpcio",
]
BLACK_VERSION = "black==19.3b0"
diff --git a/tests/compute_engine/test_credentials.py b/tests/compute_engine/test_credentials.py
index f05a566..b861984 100644
--- a/tests/compute_engine/test_credentials.py
+++ b/tests/compute_engine/test_credentials.py
@@ -11,17 +11,19 @@
# 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.
-
+import base64
import datetime
import mock
import pytest
+import responses
from google.auth import _helpers
from google.auth import exceptions
from google.auth import jwt
from google.auth import transport
from google.auth.compute_engine import credentials
+from google.auth.transport import requests
class TestCredentials(object):
@@ -270,6 +272,84 @@ class TestIDTokenCredentials(object):
"target_audience": "https://actually.not",
}
+ # Check that the signer have been initialized with a Request object
+ assert isinstance(self.credentials._signer._request, transport.Request)
+
+ @responses.activate
+ def test_with_target_audience_integration(self):
+ """ Test that it is possible to refresh credentials
+ generated from `with_target_audience`.
+
+ Instead of mocking the methods, the HTTP responses
+ have been mocked.
+ """
+
+ # mock information about credentials
+ responses.add(
+ responses.GET,
+ "http://metadata.google.internal/computeMetadata/v1/instance/"
+ "service-accounts/default/?recursive=true",
+ status=200,
+ content_type="application/json",
+ json={
+ "scopes": "email",
+ "email": "service-account@example.com",
+ "aliases": ["default"],
+ },
+ )
+
+ # mock token for credentials
+ responses.add(
+ responses.GET,
+ "http://metadata.google.internal/computeMetadata/v1/instance/"
+ "service-accounts/service-account@example.com/token",
+ status=200,
+ content_type="application/json",
+ json={
+ "access_token": "some-token",
+ "expires_in": 3210,
+ "token_type": "Bearer",
+ },
+ )
+
+ # mock sign blob endpoint
+ signature = base64.b64encode(b"some-signature").decode("utf-8")
+ responses.add(
+ responses.POST,
+ "https://iam.googleapis.com/v1/projects/-/serviceAccounts/"
+ "service-account@example.com:signBlob?alt=json",
+ status=200,
+ content_type="application/json",
+ json={"keyId": "some-key-id", "signature": signature},
+ )
+
+ id_token = "{}.{}.{}".format(
+ base64.b64encode(b'{"some":"some"}').decode("utf-8"),
+ base64.b64encode(b'{"exp": 3210}').decode("utf-8"),
+ base64.b64encode(b"token").decode("utf-8"),
+ )
+
+ # mock id token endpoint
+ responses.add(
+ responses.POST,
+ "https://www.googleapis.com/oauth2/v4/token",
+ status=200,
+ content_type="application/json",
+ json={"id_token": id_token, "expiry": 3210},
+ )
+
+ self.credentials = credentials.IDTokenCredentials(
+ request=requests.Request(),
+ service_account_email="service-account@example.com",
+ target_audience="https://audience.com",
+ )
+
+ self.credentials = self.credentials.with_target_audience("https://actually.not")
+
+ self.credentials.refresh(requests.Request())
+
+ assert self.credentials.token is not None
+
@mock.patch(
"google.auth._helpers.utcnow",
return_value=datetime.datetime.utcfromtimestamp(0),