Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jwt.Credentials.from_signing_credentials, remove serivce_account.Credentials.to_jwt_credentials #120

Merged
merged 2 commits into from
Feb 24, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions google/auth/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@

from google.auth import _helpers
from google.auth import _service_account_info
from google.auth import credentials
from google.auth import crypt
import google.auth.credentials


_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in sections
Expand Down Expand Up @@ -239,8 +239,8 @@ def decode(token, certs=None, verify=True, audience=None):
return payload


class Credentials(credentials.Signing,
credentials.Credentials):
class Credentials(google.auth.credentials.Signing,
google.auth.credentials.Credentials):
"""Credentials that use a JWT as the bearer token.

These credentials require an "audience" claim. This claim identifies the
Expand Down Expand Up @@ -371,6 +371,40 @@ def from_service_account_file(cls, filename, **kwargs):
filename, require=['client_email'])
return cls._from_signer_and_info(signer, info, **kwargs)

@classmethod
def from_signing_credentials(cls, credentials, audience, **kwargs):
"""Creates a new :class:`google.auth.jwt.Credentials` instance from an
existing :class:`google.auth.credentials.Signing` instance.

The new instance will use the same signer as the existing instance and
will use the existing instance's signer email as the issuer and
subject by default.

Example::

svc_creds = service_account.Credentials.from_service_account_file(
'service_account.json')
jwt_ceds = jwt.Credentials.from_signing_credentials(

This comment was marked as spam.

This comment was marked as spam.

svc_creds,
audience='https://speech.googleapis.com')

This comment was marked as spam.

This comment was marked as spam.


Args:
credentials (google.auth.credentials.Signing): The credentials to
use to construct the new credentials.
audience (str): the `aud` claim. The intended audience for the
credentials.
kwargs: Additional arguments to pass to the constructor.

Returns:
google.auth.jwt.Credentials: A new Credentials instance.
"""
kwargs.setdefault('issuer', credentials.signer_email)
kwargs.setdefault('subject', credentials.signer_email)
return cls(
credentials.signer,
audience=audience,
**kwargs)

def with_claims(self, issuer=None, subject=None, audience=None,
additional_claims=None):
"""Returns a copy of these credentials with modified claims.
Expand Down Expand Up @@ -431,16 +465,16 @@ def refresh(self, request):
# (pylint doesn't correctly recognize overridden methods.)
self.token, self.expiry = self._make_jwt()

@_helpers.copy_docstring(credentials.Signing)
@_helpers.copy_docstring(google.auth.credentials.Signing)
def sign_bytes(self, message):
return self._signer.sign(message)

@property
@_helpers.copy_docstring(credentials.Signing)
@_helpers.copy_docstring(google.auth.credentials.Signing)
def signer_email(self):
return self._issuer

@property
@_helpers.copy_docstring(credentials.Signing)
@_helpers.copy_docstring(google.auth.credentials.Signing)
def signer(self):
return self._signer
32 changes: 0 additions & 32 deletions google/oauth2/service_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,38 +204,6 @@ def from_service_account_file(cls, filename, **kwargs):
filename, require=['client_email', 'token_uri'])
return cls._from_signer_and_info(signer, info, **kwargs)

def to_jwt_credentials(self, audience):
"""Creates a :class:`google.auth.jwt.Credentials` instance from this
instance.

The new instance will use the same private key as this instance and
will use this instance's service account email as the issuer and
subject.

This is the same as calling
:meth:`jwt.Credentials.from_service_account_file` with the same
file used to create these credentials::

svc_creds = service_account.Credentials.from_service_account_file(
'service_account.json')
jwt_from_svc = svc_credentials.to_jwt_credentials()
# is the same as:
jwt_creds = jwt.Credentials.from_service_account_file(
'service_account.json')

Args:
audience (str): the `aud` claim. The intended audience for the
credentials.

Returns:
google.auth.jwt.Credentials: A new Credentials instance.
"""
return jwt.Credentials(
self._signer,
issuer=self._service_account_email,
subject=self._service_account_email,
audience=audience)

@property
def service_account_email(self):
"""The service account email."""
Expand Down
4 changes: 3 additions & 1 deletion system_tests/test_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import google.auth
import google.auth.credentials
import google.auth.jwt
import google.auth.transport.grpc
from google.cloud.gapic.pubsub.v1 import publisher_client

Expand Down Expand Up @@ -42,7 +43,8 @@ def test_grpc_request_with_jwt_credentials(http_request):
credentials, project_id = google.auth.default()
audience = 'https://{}/google.pubsub.v1.Publisher'.format(
publisher_client.PublisherClient.SERVICE_ADDRESS)
credentials = credentials.to_jwt_credentials(
credentials = google.auth.jwt.Credentials.from_signing_credentials(
credentials,
audience=audience)

channel = google.auth.transport.grpc.secure_authorized_channel(
Expand Down
13 changes: 0 additions & 13 deletions tests/oauth2/test_service_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,6 @@ def test_from_service_account_file_args(self):
assert credentials._subject == subject
assert credentials._additional_claims == additional_claims

def test_to_jwt_credentials(self):
jwt_from_svc = self.credentials.to_jwt_credentials(
audience=mock.sentinel.audience)
jwt_from_info = jwt.Credentials.from_service_account_info(
SERVICE_ACCOUNT_INFO,
audience=mock.sentinel.audience)

assert isinstance(jwt_from_svc, jwt.Credentials)
assert jwt_from_svc._signer.key_id == jwt_from_info._signer.key_id
assert jwt_from_svc._issuer == jwt_from_info._issuer
assert jwt_from_svc._subject == jwt_from_info._subject
assert jwt_from_svc._audience == jwt_from_info._audience

def test_default_state(self):
assert not self.credentials.valid
# Expiration hasn't been set yet
Expand Down
14 changes: 14 additions & 0 deletions tests/test_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,20 @@ def test_from_service_account_file_args(self):
assert credentials._audience == self.AUDIENCE
assert credentials._additional_claims == self.ADDITIONAL_CLAIMS

def test_from_signing_credentials(self):
jwt_from_signing = self.credentials.from_signing_credentials(
self.credentials,
audience=mock.sentinel.new_audience)
jwt_from_info = jwt.Credentials.from_service_account_info(
SERVICE_ACCOUNT_INFO,
audience=mock.sentinel.new_audience)

assert isinstance(jwt_from_signing, jwt.Credentials)
assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
assert jwt_from_signing._issuer == jwt_from_info._issuer
assert jwt_from_signing._subject == jwt_from_info._subject
assert jwt_from_signing._audience == jwt_from_info._audience

def test_default_state(self):
assert not self.credentials.valid
# Expiration hasn't been set yet
Expand Down