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