Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firebase_admin/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def create_custom_token(self, uid, developer_claims=None):
developer_claims: A dictionary of claims to be included in the token.

Returns:
string: A token string minted from the input parameters.
string: A token minted from the input parameters as a byte string.

Raises:
ValueError: If input parameters are invalid.
Expand Down
70 changes: 47 additions & 23 deletions firebase_admin/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""Firebase credentials module."""
import collections
import json
import six

import google.auth
from google.auth.transport import requests
Expand Down Expand Up @@ -55,32 +56,41 @@ class Certificate(Base):

_CREDENTIAL_TYPE = 'service_account'

def __init__(self, file_path):
"""Initializes a credential from a certificate file.
def __init__(self, cert):
"""Initializes a credential from a Google service account certificate.

Parses the specified certificate file (service account file), and
creates a credential instance from it.
Service account certificates can be downloaded as JSON files from the Firebase console.
To instantiate a credential from a certificate file, either specify the file path or a
dict representing the parsed contents of the file.

Args:
file_path: Path to a service account certificate file.
cert: Path to a certificate file or a dict representing the contents of a certificate.

Raises:
IOError: If the specified file doesn't exist or cannot be read.
ValueError: If the certificate file is invalid.
IOError: If the specified certificate file doesn't exist or cannot be read.
ValueError: If the specified certificate is invalid.
"""
super(Certificate, self).__init__()
with open(file_path) as json_keyfile:
json_data = json.load(json_keyfile)
if isinstance(cert, six.string_types):
with open(cert) as json_file:
json_data = json.load(json_file)
elif isinstance(cert, dict):
json_data = cert
else:
raise ValueError(
'Invalid certificate argument: "{0}". Certificate argument must be a file path, '
'or a dict containing the parsed file contents.'.format(cert))

if json_data.get('type') != self._CREDENTIAL_TYPE:
raise ValueError('Invalid certificate file: "{0}". File must contain a '
'"type" field set to "{1}".'.format(file_path, self._CREDENTIAL_TYPE))
raise ValueError('Invalid service account certificate. Certificate must contain a '
'"type" field set to "{0}".'.format(self._CREDENTIAL_TYPE))
self._project_id = json_data.get('project_id')
try:
self._g_credential = service_account.Credentials.from_service_account_info(
json_data, scopes=_scopes)
except ValueError as error:
raise ValueError('Failed to initialize a certificate credential from file "{0}". '
'Caused by: "{1}"'.format(file_path, error))
raise ValueError('Failed to initialize a certificate credential. '
'Caused by: "{0}"'.format(error))

@property
def project_id(self):
Expand Down Expand Up @@ -132,29 +142,43 @@ class RefreshToken(Base):

_CREDENTIAL_TYPE = 'authorized_user'

def __init__(self, file_path):
"""Initializes a refresh token credential from the specified JSON file.
def __init__(self, refresh_token):
"""Initializes a credential from a refresh token JSON file.

The JSON must consist of client_id, client_secert and refresh_token fields. Refresh
token files are typically created and managed by the gcloud SDK. To instantiate
a credential from a refresh token file, either specify the file path or a dict
representing the parsed contents of the file.

Args:
file_path: File path to a refresh token JSON file.
refresh_token: Path to a refresh token file or a dict representing the contents of a
refresh token file.

Raises:
IOError: If the specified file doesn't exist or cannot be read.
ValueError: If the refresh token file is invalid.
ValueError: If the refresh token configuration is invalid.
"""
super(RefreshToken, self).__init__()
with open(file_path) as json_keyfile:
json_data = json.load(json_keyfile)
if isinstance(refresh_token, six.string_types):
with open(refresh_token) as json_file:
json_data = json.load(json_file)
elif isinstance(refresh_token, dict):
json_data = refresh_token
else:
raise ValueError(
'Invalid refresh token argument: "{0}". Refresh token argument must be a file '
'path, or a dict containing the parsed file contents.'.format(refresh_token))

if json_data.get('type') != self._CREDENTIAL_TYPE:
raise ValueError('Invalid refresh token file: "{0}". File must contain a '
'"type" field set to "{1}".'.format(file_path, self._CREDENTIAL_TYPE))
raise ValueError('Invalid refresh token configuration. JSON must contain a '
'"type" field set to "{0}".'.format(self._CREDENTIAL_TYPE))
try:
client_id = json_data['client_id']
client_secret = json_data['client_secret']
refresh_token = json_data['refresh_token']
except KeyError as error:
raise ValueError('Failed to initialize a refresh token credential from file "{0}". '
'Caused by: "{1}"'.format(file_path, error))
raise ValueError('Failed to initialize a refresh token credential. '
'Caused by: "{0}"'.format(error))
self._g_credential = credentials.Credentials(
token=None, refresh_token=refresh_token,
token_uri='https://accounts.google.com/o/oauth2/token',
Expand Down
56 changes: 41 additions & 15 deletions tests/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ class TestCertificate(object):
def test_init_from_file(self):
credential = credentials.Certificate(
testutils.resource_filename('service_account.json'))
self._verify_credential(credential)

def test_init_from_dict(self):
parsed_json = json.loads(testutils.resource('service_account.json'))
credential = credentials.Certificate(parsed_json)
self._verify_credential(credential)

@pytest.mark.parametrize('file_name,error', invalid_certs.values(), ids=list(invalid_certs))
def test_init_from_invalid_certificate(self, file_name, error):
with pytest.raises(error):
credentials.Certificate(testutils.resource_filename(file_name))

@pytest.mark.parametrize('arg', [None, 0, 1, True, False, list(), tuple(), dict()])
def test_invalid_args(self, arg):
with pytest.raises(ValueError):
credentials.Certificate(arg)

def _verify_credential(self, credential):
assert credential.project_id == 'mock-project-id'
assert credential.service_account_email == 'mock-email@mock-project.iam.gserviceaccount.com'
assert isinstance(credential.signer, crypt.Signer)
Expand All @@ -59,11 +77,6 @@ def test_init_from_file(self):
assert access_token.access_token == 'mock_access_token'
assert isinstance(access_token.expiry, datetime.datetime)

@pytest.mark.parametrize('file_name,error', invalid_certs.values(), ids=list(invalid_certs))
def test_init_from_invalid_certificate(self, file_name, error):
with pytest.raises(error):
credentials.Certificate(testutils.resource_filename(file_name))


@pytest.fixture
def app_default(request):
Expand Down Expand Up @@ -110,6 +123,29 @@ class TestRefreshToken(object):
def test_init_from_file(self):
credential = credentials.RefreshToken(
testutils.resource_filename('refresh_token.json'))
self._verify_credential(credential)

def test_init_from_dict(self):
parsed_json = json.loads(testutils.resource('refresh_token.json'))
credential = credentials.RefreshToken(parsed_json)
self._verify_credential(credential)

def test_init_from_nonexisting_file(self):
with pytest.raises(IOError):
credentials.RefreshToken(
testutils.resource_filename('non_existing.json'))

def test_init_from_invalid_file(self):
with pytest.raises(ValueError):
credentials.RefreshToken(
testutils.resource_filename('service_account.json'))

@pytest.mark.parametrize('arg', [None, 0, 1, True, False, list(), tuple(), dict()])
def test_invalid_args(self, arg):
with pytest.raises(ValueError):
credentials.RefreshToken(arg)

def _verify_credential(self, credential):
assert credential.client_id == 'mock.apps.googleusercontent.com'
assert credential.client_secret == 'mock-secret'
assert credential.refresh_token == 'mock-refresh-token'
Expand All @@ -127,13 +163,3 @@ def test_init_from_file(self):
access_token = credential.get_access_token()
assert access_token.access_token == 'mock_access_token'
assert isinstance(access_token.expiry, datetime.datetime)

def test_init_from_nonexisting_file(self):
with pytest.raises(IOError):
credentials.RefreshToken(
testutils.resource_filename('non_existing.json'))

def test_init_from_invalid_file(self):
with pytest.raises(ValueError):
credentials.RefreshToken(
testutils.resource_filename('service_account.json'))