Fix cyclic import and add Signer.from_service_account_info (#99)
diff --git a/google/auth/_service_account_info.py b/google/auth/_service_account_info.py
index b007abd..9891011 100644
--- a/google/auth/_service_account_info.py
+++ b/google/auth/_service_account_info.py
@@ -41,10 +41,7 @@
ValueError: if the data was in the wrong format, or if one of the
required keys is missing.
"""
- # Private key is always required.
- keys_needed = set(('private_key',))
- if require is not None:
- keys_needed.update(require)
+ keys_needed = set(require if require is not None else [])
missing = keys_needed.difference(six.iterkeys(data))
@@ -54,8 +51,7 @@
'fields {}.'.format(', '.join(missing)))
# Create a signer.
- signer = crypt.Signer.from_string(
- data['private_key'], data.get('private_key_id'))
+ signer = crypt.Signer.from_service_account_info(data)
return signer
diff --git a/google/auth/crypt.py b/google/auth/crypt.py
index 618afe5..c425045 100644
--- a/google/auth/crypt.py
+++ b/google/auth/crypt.py
@@ -38,6 +38,8 @@
signature = signer.sign(message)
"""
+import io
+import json
from pyasn1.codec.der import decoder
from pyasn1_modules import pem
@@ -55,6 +57,8 @@
_PKCS8_MARKER = ('-----BEGIN PRIVATE KEY-----',
'-----END PRIVATE KEY-----')
_PKCS8_SPEC = PrivateKeyInfo()
+_JSON_FILE_PRIVATE_KEY = 'private_key'
+_JSON_FILE_PRIVATE_KEY_ID = 'private_key_id'
def _bit_list_to_bytes(bit_list):
@@ -229,6 +233,30 @@
return cls(private_key, key_id=key_id)
@classmethod
+ def from_service_account_info(cls, info):
+ """Creates a Signer instance instance from a dictionary containing
+ service account info in Google format.
+
+ Args:
+ info (Mapping[str, str]): The service account info in Google
+ format.
+
+ Returns:
+ Signer: The constructed signer.
+
+ Raises:
+ ValueError: If the info is not in the expected format.
+ """
+ if _JSON_FILE_PRIVATE_KEY not in info:
+ raise ValueError(
+ 'The private_key field was not found in the service account '
+ 'info.')
+
+ return cls.from_string(
+ info[_JSON_FILE_PRIVATE_KEY],
+ info.get(_JSON_FILE_PRIVATE_KEY_ID))
+
+ @classmethod
def from_service_account_file(cls, filename):
"""Creates a Signer instance from a service account .json file
in Google format.
@@ -239,6 +267,7 @@
Returns:
Signer: The constructed signer.
"""
- from google.auth import _service_account_info
- _, signer = _service_account_info.from_filename(filename)
- return signer
+ with io.open(filename, 'r', encoding='utf-8') as json_file:
+ data = json.load(json_file)
+
+ return cls.from_service_account_info(data)
diff --git a/tests/test__service_account_info.py b/tests/test__service_account_info.py
index 14f659a..4caea95 100644
--- a/tests/test__service_account_info.py
+++ b/tests/test__service_account_info.py
@@ -47,7 +47,7 @@
def test_from_dict_bad_format():
with pytest.raises(ValueError) as excinfo:
- _service_account_info.from_dict({})
+ _service_account_info.from_dict({}, require=('meep',))
assert excinfo.match(r'missing fields')
diff --git a/tests/test_crypt.py b/tests/test_crypt.py
index fd70f4b..9671230 100644
--- a/tests/test_crypt.py
+++ b/tests/test_crypt.py
@@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
import json
+import os
import mock
from pyasn1_modules import pem
@@ -199,9 +199,23 @@
with pytest.raises(ValueError):
crypt.Signer.from_string(key_bytes)
+ def test_from_service_account_info(self):
+ signer = crypt.Signer.from_service_account_info(SERVICE_ACCOUNT_INFO)
+
+ assert signer.key_id == SERVICE_ACCOUNT_INFO[
+ crypt._JSON_FILE_PRIVATE_KEY_ID]
+ assert isinstance(signer._key, rsa.key.PrivateKey)
+
+ def test_from_service_account_info_missing_key(self):
+ with pytest.raises(ValueError) as excinfo:
+ crypt.Signer.from_service_account_info({})
+
+ assert excinfo.match(crypt._JSON_FILE_PRIVATE_KEY)
+
def test_from_service_account_file(self):
signer = crypt.Signer.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE)
- assert signer.key_id == SERVICE_ACCOUNT_INFO['private_key_id']
+ assert signer.key_id == SERVICE_ACCOUNT_INFO[
+ crypt._JSON_FILE_PRIVATE_KEY_ID]
assert isinstance(signer._key, rsa.key.PrivateKey)