Skip to content

Commit

Permalink
Fix google.auth.compute_engine.credentials.IDTokenCredentials.with_ta…
Browse files Browse the repository at this point in the history
…rget_audience method (googleapis#359)
  • Loading branch information
lmiglio committed Jul 30, 2019
1 parent ccf2e50 commit e446ec8
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ tests/data/user-key.json

# PyCharm configuration:
.idea
venv/

# Generated files
pylintrc
pylintrc.test
pytype_output/

.python-version
23 changes: 16 additions & 7 deletions google/auth/compute_engine/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ class IDTokenCredentials(credentials.Credentials, credentials.Signing):
def __init__(self, request, target_audience,
token_uri=_DEFAULT_TOKEN_URI,
additional_claims=None,
service_account_email=None):
service_account_email=None,
signer=None):
"""
Args:
request (google.auth.transport.Request): The object used to make
Expand All @@ -145,6 +146,9 @@ def __init__(self, request, target_audience,
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__()

Expand All @@ -153,10 +157,12 @@ def __init__(self, request, target_audience,
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
Expand All @@ -176,12 +182,15 @@ def with_target_audience(self, target_audience):
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())
additional_claims=self._additional_claims.copy(),
signer=self.signer)

def _make_authorization_grant_assertion(self):
"""Create the OAuth 2.0 assertion.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

setup(
name='google-auth',
version='1.6.3',
version='1.6.4',
author='Google Cloud Platform',
author_email='jonwayne+google-auth@google.com',
description='Google Authentication Library',
Expand Down
87 changes: 86 additions & 1 deletion tests/compute_engine/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -257,6 +259,89 @@ def test_with_target_audience(self, sign, get, utcnow):
'iss': 'service-account@example.com',
'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))
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ deps =
requests-oauthlib
urllib3
cryptography
responses
grpcio; platform_python_implementation != 'PyPy'

commands =
pytest --cov=google.auth --cov=google.oauth2 --cov=tests {posargs:tests}

Expand Down

0 comments on commit e446ec8

Please sign in to comment.