2929from oauth2client import service_account
3030import pytz
3131
32+ try :
33+ from google .appengine .api import app_identity
34+ except ImportError :
35+ app_identity = None
36+
37+ try :
38+ from oauth2client .appengine import AppAssertionCredentials as _GAECreds
39+ except ImportError :
40+ _GAECreds = None
41+
3242
3343def get_credentials ():
3444 """Gets credentials implicitly from the current environment.
@@ -160,7 +170,66 @@ def _get_pem_key(credentials):
160170 return RSA .importKey (pem_text )
161171
162172
163- def _get_signed_query_params (credentials , expiration , signature_string ):
173+ def _get_signature_bytes (credentials , string_to_sign ):
174+ """Uses crypto attributes of credentials to sign a string/bytes.
175+
176+ :type credentials: :class:`client.SignedJwtAssertionCredentials`,
177+ :class:`service_account._ServiceAccountCredentials`,
178+ :class:`_GAECreds`
179+ :param credentials: The credentials used for signing text (typically
180+ involves the creation of an RSA key).
181+
182+ :type string_to_sign: string
183+ :param string_to_sign: The string to be signed by the credentials.
184+
185+ :rtype: bytes
186+ :returns: Signed bytes produced by the credentials.
187+ """
188+ if _GAECreds is not None and isinstance (credentials , _GAECreds ):
189+ _ , signed_bytes = app_identity .sign_blob (string_to_sign )
190+ return signed_bytes
191+ else :
192+ pem_key = _get_pem_key (credentials )
193+ # Sign the string with the RSA key.
194+ signer = PKCS1_v1_5 .new (pem_key )
195+ if not isinstance (string_to_sign , six .binary_type ):
196+ string_to_sign = string_to_sign .encode ('utf-8' )
197+ signature_hash = SHA256 .new (string_to_sign )
198+ return signer .sign (signature_hash )
199+
200+
201+ def _get_service_account_name (credentials ):
202+ """Determines service account name from a credentials object.
203+
204+ :type credentials: :class:`client.SignedJwtAssertionCredentials`,
205+ :class:`service_account._ServiceAccountCredentials`,
206+ :class:`_GAECreds`
207+ :param credentials: The credentials used to determine the service
208+ account name.
209+
210+ :type string_to_sign: string
211+ :param string_to_sign: The string to be signed by the credentials.
212+
213+ :rtype: bytes
214+ :returns: Signed bytes produced by the credentials.
215+ :raises: :class:`ValueError` if the credentials are not a valid service
216+ account type
217+ """
218+ service_account_name = None
219+ if isinstance (credentials , client .SignedJwtAssertionCredentials ):
220+ service_account_name = credentials .service_account_name
221+ elif isinstance (credentials , service_account ._ServiceAccountCredentials ):
222+ service_account_name = credentials ._service_account_email
223+ elif _GAECreds is not None and isinstance (credentials , _GAECreds ):
224+ service_account_name = app_identity .get_service_account_name ()
225+
226+ if service_account_name is None :
227+ raise ValueError ('Service account name could not be determined '
228+ 'from credentials' )
229+ return service_account_name
230+
231+
232+ def _get_signed_query_params (credentials , expiration , string_to_sign ):
164233 """Gets query parameters for creating a signed URL.
165234
166235 :type credentials: :class:`client.SignedJwtAssertionCredentials`,
@@ -171,27 +240,16 @@ def _get_signed_query_params(credentials, expiration, signature_string):
171240 :type expiration: int or long
172241 :param expiration: When the signed URL should expire.
173242
174- :type signature_string : string
175- :param signature_string : The string to be signed by the credentials.
243+ :type string_to_sign : string
244+ :param string_to_sign : The string to be signed by the credentials.
176245
177246 :rtype: dict
178247 :returns: Query parameters matching the signing credentials with a
179248 signed payload.
180249 """
181- pem_key = _get_pem_key (credentials )
182- # Sign the string with the RSA key.
183- signer = PKCS1_v1_5 .new (pem_key )
184- if not isinstance (signature_string , six .binary_type ):
185- signature_string = signature_string .encode ('utf-8' )
186- signature_hash = SHA256 .new (signature_string )
187- signature_bytes = signer .sign (signature_hash )
250+ signature_bytes = _get_signature_bytes (credentials , string_to_sign )
188251 signature = base64 .b64encode (signature_bytes )
189-
190- if isinstance (credentials , client .SignedJwtAssertionCredentials ):
191- service_account_name = credentials .service_account_name
192- elif isinstance (credentials , service_account ._ServiceAccountCredentials ):
193- service_account_name = credentials ._service_account_email
194- # We know one of the above must occur since `_get_pem_key` fails if not.
252+ service_account_name = _get_service_account_name (credentials )
195253 return {
196254 'GoogleAccessId' : service_account_name ,
197255 'Expires' : str (expiration ),
@@ -277,7 +335,7 @@ def generate_signed_url(credentials, resource, expiration,
277335 expiration = _get_expiration_seconds (expiration )
278336
279337 # Generate the string to sign.
280- signature_string = '\n ' .join ([
338+ string_to_sign = '\n ' .join ([
281339 method ,
282340 content_md5 or '' ,
283341 content_type or '' ,
@@ -287,7 +345,7 @@ def generate_signed_url(credentials, resource, expiration,
287345 # Set the right query parameters.
288346 query_params = _get_signed_query_params (credentials ,
289347 expiration ,
290- signature_string )
348+ string_to_sign )
291349
292350 # Return the built URL.
293351 return '{endpoint}{resource}?{querystring}' .format (
0 commit comments