162 lines
5.7 KiB
Python
162 lines
5.7 KiB
Python
# Copyright 2014 Google Inc. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# 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.
|
|
|
|
"""Utilities for Google Compute Engine
|
|
|
|
Utilities for making it easier to use OAuth 2.0 on Google Compute Engine.
|
|
"""
|
|
|
|
import logging
|
|
import warnings
|
|
|
|
import httplib2
|
|
|
|
from oauth2client import client
|
|
from oauth2client.contrib import _metadata
|
|
|
|
|
|
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_SCOPES_WARNING = """\
|
|
You have requested explicit scopes to be used with a GCE service account.
|
|
Using this argument will have no effect on the actual scopes for tokens
|
|
requested. These scopes are set at VM instance creation time and
|
|
can't be overridden in the request.
|
|
"""
|
|
|
|
|
|
class AppAssertionCredentials(client.AssertionCredentials):
|
|
"""Credentials object for Compute Engine Assertion Grants
|
|
|
|
This object will allow a Compute Engine instance to identify itself to
|
|
Google and other OAuth 2.0 servers that can verify assertions. It can be
|
|
used for the purpose of accessing data stored under an account assigned to
|
|
the Compute Engine instance itself.
|
|
|
|
This credential does not require a flow to instantiate because it
|
|
represents a two legged flow, and therefore has all of the required
|
|
information to generate and refresh its own access tokens.
|
|
|
|
Note that :attr:`service_account_email` and :attr:`scopes`
|
|
will both return None until the credentials have been refreshed.
|
|
To check whether credentials have previously been refreshed use
|
|
:attr:`invalid`.
|
|
"""
|
|
|
|
def __init__(self, email=None, *args, **kwargs):
|
|
"""Constructor for AppAssertionCredentials
|
|
|
|
Args:
|
|
email: an email that specifies the service account to use.
|
|
Only necessary if using custom service accounts
|
|
(see https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#createdefaultserviceaccount).
|
|
"""
|
|
if 'scopes' in kwargs:
|
|
warnings.warn(_SCOPES_WARNING)
|
|
kwargs['scopes'] = None
|
|
|
|
# Assertion type is no longer used, but still in the
|
|
# parent class signature.
|
|
super(AppAssertionCredentials, self).__init__(None, *args, **kwargs)
|
|
|
|
self.service_account_email = email
|
|
self.scopes = None
|
|
self.invalid = True
|
|
|
|
@classmethod
|
|
def from_json(cls, json_data):
|
|
raise NotImplementedError(
|
|
'Cannot serialize credentials for GCE service accounts.')
|
|
|
|
def to_json(self):
|
|
raise NotImplementedError(
|
|
'Cannot serialize credentials for GCE service accounts.')
|
|
|
|
def retrieve_scopes(self, http):
|
|
"""Retrieves the canonical list of scopes for this access token.
|
|
|
|
Overrides client.Credentials.retrieve_scopes. Fetches scopes info
|
|
from the metadata server.
|
|
|
|
Args:
|
|
http: httplib2.Http, an http object to be used to make the refresh
|
|
request.
|
|
|
|
Returns:
|
|
A set of strings containing the canonical list of scopes.
|
|
"""
|
|
self._retrieve_info(http.request)
|
|
return self.scopes
|
|
|
|
def _retrieve_info(self, http_request):
|
|
"""Validates invalid service accounts by retrieving service account info.
|
|
|
|
Args:
|
|
http_request: callable, a callable that matches the method
|
|
signature of httplib2.Http.request, used to make the
|
|
request to the metadata server
|
|
"""
|
|
if self.invalid:
|
|
info = _metadata.get_service_account_info(
|
|
http_request,
|
|
service_account=self.service_account_email or 'default')
|
|
self.invalid = False
|
|
self.service_account_email = info['email']
|
|
self.scopes = info['scopes']
|
|
|
|
def _refresh(self, http_request):
|
|
"""Refreshes the access_token.
|
|
|
|
Skip all the storage hoops and just refresh using the API.
|
|
|
|
Args:
|
|
http_request: callable, a callable that matches the method
|
|
signature of httplib2.Http.request, used to make
|
|
the refresh request.
|
|
|
|
Raises:
|
|
HttpAccessTokenRefreshError: When the refresh fails.
|
|
"""
|
|
try:
|
|
self._retrieve_info(http_request)
|
|
self.access_token, self.token_expiry = _metadata.get_token(
|
|
http_request, service_account=self.service_account_email)
|
|
except httplib2.HttpLib2Error as e:
|
|
raise client.HttpAccessTokenRefreshError(str(e))
|
|
|
|
@property
|
|
def serialization_data(self):
|
|
raise NotImplementedError(
|
|
'Cannot serialize credentials for GCE service accounts.')
|
|
|
|
def create_scoped_required(self):
|
|
return False
|
|
|
|
def sign_blob(self, blob):
|
|
"""Cryptographically sign a blob (of bytes).
|
|
|
|
This method is provided to support a common interface, but
|
|
the actual key used for a Google Compute Engine service account
|
|
is not available, so it can't be used to sign content.
|
|
|
|
Args:
|
|
blob: bytes, Message to be signed.
|
|
|
|
Raises:
|
|
NotImplementedError, always.
|
|
"""
|
|
raise NotImplementedError(
|
|
'Compute Engine service accounts cannot sign blobs')
|