1# Copyright 2014 Google Inc. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Utilities for Google Compute Engine 16 17Utilities for making it easier to use OAuth 2.0 on Google Compute Engine. 18""" 19 20import logging 21import warnings 22 23import httplib2 24 25from oauth2client import client 26from oauth2client.contrib import _metadata 27 28 29__author__ = 'jcgregorio@google.com (Joe Gregorio)' 30 31logger = logging.getLogger(__name__) 32 33_SCOPES_WARNING = """\ 34You have requested explicit scopes to be used with a GCE service account. 35Using this argument will have no effect on the actual scopes for tokens 36requested. These scopes are set at VM instance creation time and 37can't be overridden in the request. 38""" 39 40 41class AppAssertionCredentials(client.AssertionCredentials): 42 """Credentials object for Compute Engine Assertion Grants 43 44 This object will allow a Compute Engine instance to identify itself to 45 Google and other OAuth 2.0 servers that can verify assertions. It can be 46 used for the purpose of accessing data stored under an account assigned to 47 the Compute Engine instance itself. 48 49 This credential does not require a flow to instantiate because it 50 represents a two legged flow, and therefore has all of the required 51 information to generate and refresh its own access tokens. 52 53 Note that :attr:`service_account_email` and :attr:`scopes` 54 will both return None until the credentials have been refreshed. 55 To check whether credentials have previously been refreshed use 56 :attr:`invalid`. 57 """ 58 59 def __init__(self, email=None, *args, **kwargs): 60 """Constructor for AppAssertionCredentials 61 62 Args: 63 email: an email that specifies the service account to use. 64 Only necessary if using custom service accounts 65 (see https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#createdefaultserviceaccount). 66 """ 67 if 'scopes' in kwargs: 68 warnings.warn(_SCOPES_WARNING) 69 kwargs['scopes'] = None 70 71 # Assertion type is no longer used, but still in the 72 # parent class signature. 73 super(AppAssertionCredentials, self).__init__(None, *args, **kwargs) 74 75 self.service_account_email = email 76 self.scopes = None 77 self.invalid = True 78 79 @classmethod 80 def from_json(cls, json_data): 81 raise NotImplementedError( 82 'Cannot serialize credentials for GCE service accounts.') 83 84 def to_json(self): 85 raise NotImplementedError( 86 'Cannot serialize credentials for GCE service accounts.') 87 88 def retrieve_scopes(self, http): 89 """Retrieves the canonical list of scopes for this access token. 90 91 Overrides client.Credentials.retrieve_scopes. Fetches scopes info 92 from the metadata server. 93 94 Args: 95 http: httplib2.Http, an http object to be used to make the refresh 96 request. 97 98 Returns: 99 A set of strings containing the canonical list of scopes. 100 """ 101 self._retrieve_info(http.request) 102 return self.scopes 103 104 def _retrieve_info(self, http_request): 105 """Validates invalid service accounts by retrieving service account info. 106 107 Args: 108 http_request: callable, a callable that matches the method 109 signature of httplib2.Http.request, used to make the 110 request to the metadata server 111 """ 112 if self.invalid: 113 info = _metadata.get_service_account_info( 114 http_request, 115 service_account=self.service_account_email or 'default') 116 self.invalid = False 117 self.service_account_email = info['email'] 118 self.scopes = info['scopes'] 119 120 def _refresh(self, http_request): 121 """Refreshes the access_token. 122 123 Skip all the storage hoops and just refresh using the API. 124 125 Args: 126 http_request: callable, a callable that matches the method 127 signature of httplib2.Http.request, used to make 128 the refresh request. 129 130 Raises: 131 HttpAccessTokenRefreshError: When the refresh fails. 132 """ 133 try: 134 self._retrieve_info(http_request) 135 self.access_token, self.token_expiry = _metadata.get_token( 136 http_request, service_account=self.service_account_email) 137 except httplib2.HttpLib2Error as e: 138 raise client.HttpAccessTokenRefreshError(str(e)) 139 140 @property 141 def serialization_data(self): 142 raise NotImplementedError( 143 'Cannot serialize credentials for GCE service accounts.') 144 145 def create_scoped_required(self): 146 return False 147 148 def sign_blob(self, blob): 149 """Cryptographically sign a blob (of bytes). 150 151 This method is provided to support a common interface, but 152 the actual key used for a Google Compute Engine service account 153 is not available, so it can't be used to sign content. 154 155 Args: 156 blob: bytes, Message to be signed. 157 158 Raises: 159 NotImplementedError, always. 160 """ 161 raise NotImplementedError( 162 'Compute Engine service accounts cannot sign blobs') 163