Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(906)

Side by Side Diff: gslib/third_party/storage_apitools/credentials_lib.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
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 """Common credentials classes and constructors."""
15
16 import json
17 import os
18 import urllib2
19
20
21 import httplib2
22 import oauth2client.client
23 import oauth2client.gce
24 import oauth2client.multistore_file
25
26 from gslib.third_party.storage_apitools import exceptions
27 from gslib.third_party.storage_apitools import util
28
29 __all__ = [
30 'CredentialsFromFile',
31 'GaeAssertionCredentials',
32 'GceAssertionCredentials',
33 'GetCredentials',
34 'ServiceAccountCredentials',
35 'ServiceAccountCredentialsFromFile',
36 ]
37
38
39 # TODO: Expose the extra args here somewhere higher up,
40 # possibly as flags in the generated CLI.
41 def GetCredentials(package_name, scopes, client_id, client_secret, user_agent,
42 credentials_filename=None,
43 service_account_name=None, service_account_keyfile=None,
44 api_key=None, client=None):
45 """Attempt to get credentials, using an oauth dance as the last resort."""
46 scopes = util.NormalizeScopes(scopes)
47 # TODO: Error checking.
48 client_info = {
49 'client_id': client_id,
50 'client_secret': client_secret,
51 'scope': ' '.join(sorted(util.NormalizeScopes(scopes))),
52 'user_agent': user_agent or '%s-generated/0.1' % package_name,
53 }
54 if service_account_name is not None:
55 credentials = ServiceAccountCredentialsFromFile(
56 service_account_name, service_account_keyfile, scopes)
57 if credentials is not None:
58 return credentials
59 credentials = GaeAssertionCredentials.Get(scopes)
60 if credentials is not None:
61 return credentials
62 credentials = GceAssertionCredentials.Get(scopes)
63 if credentials is not None:
64 return credentials
65 credentials_filename = credentials_filename or os.path.expanduser(
66 '~/.apitools.token')
67 credentials = CredentialsFromFile(credentials_filename, client_info)
68 if credentials is not None:
69 return credentials
70 raise exceptions.CredentialsError('Could not create valid credentials')
71
72
73 def ServiceAccountCredentialsFromFile(
74 service_account_name, private_key_filename, scopes):
75 with open(private_key_filename) as key_file:
76 return ServiceAccountCredentials(
77 service_account_name, key_file.read(), scopes)
78
79
80 def ServiceAccountCredentials(service_account_name, private_key, scopes):
81 scopes = util.NormalizeScopes(scopes)
82 return oauth2client.client.SignedJwtAssertionCredentials(
83 service_account_name, private_key, scopes)
84
85
86 # TODO: We override to add some utility code, and to
87 # update the old refresh implementation. Either push this code into
88 # oauth2client or drop oauth2client.
89 class GceAssertionCredentials(oauth2client.gce.AppAssertionCredentials):
90 """Assertion credentials for GCE instances."""
91
92 def __init__(self, scopes=None, service_account_name='default', **kwds):
93 """Initializes the credentials instance.
94
95 Args:
96 scopes: The scopes to get. If None, whatever scopes that are available
97 to the instance are used.
98 service_account_name: The service account to retrieve the scopes from.
99 **kwds: Additional keyword args.
100 """
101 if not util.DetectGce():
102 raise exceptions.ResourceUnavailableError(
103 'GCE credentials requested outside a GCE instance')
104 if not self.GetServiceAccount(service_account_name):
105 raise exceptions.ResourceUnavailableError(
106 'GCE credentials requested but service account %s does not exist.' %
107 service_account_name)
108 self.__service_account_name = service_account_name
109 if scopes:
110 scope_ls = util.NormalizeScopes(scopes)
111 instance_scopes = self.GetInstanceScopes()
112 if scope_ls > instance_scopes:
113 raise exceptions.CredentialsError(
114 'Instance did not have access to scopes %s' % (
115 sorted(list(scope_ls - instance_scopes)),))
116 else:
117 scopes = self.GetInstanceScopes()
118 super(GceAssertionCredentials, self).__init__(scopes, **kwds)
119
120 @classmethod
121 def Get(cls, *args, **kwds):
122 try:
123 return cls(*args, **kwds)
124 except exceptions.Error:
125 return None
126
127 def GetServiceAccount(self, account):
128 account_uri = (
129 'http://metadata.google.internal/computeMetadata/'
130 'v1/instance/service-accounts')
131 additional_headers = {'X-Google-Metadata-Request': 'True'}
132 request = urllib2.Request(account_uri, headers=additional_headers)
133 try:
134 response = urllib2.urlopen(request)
135 except urllib2.URLError as e:
136 raise exceptions.CommunicationError(
137 'Could not reach metadata service: %s' % e.reason)
138 response_lines = [line.rstrip('/\n\r') for line in response.readlines()]
139 return account in response_lines
140
141 def GetInstanceScopes(self):
142 # Extra header requirement can be found here:
143 # https://developers.google.com/compute/docs/metadata
144 scopes_uri = (
145 'http://metadata.google.internal/computeMetadata/v1/instance/'
146 'service-accounts/%s/scopes') % self.__service_account_name
147 additional_headers = {'X-Google-Metadata-Request': 'True'}
148 request = urllib2.Request(scopes_uri, headers=additional_headers)
149 try:
150 response = urllib2.urlopen(request)
151 except urllib2.URLError as e:
152 raise exceptions.CommunicationError(
153 'Could not reach metadata service: %s' % e.reason)
154 return util.NormalizeScopes(scope.strip() for scope in response.readlines())
155
156 def _refresh(self, do_request): # pylint: disable=g-bad-name
157 """Refresh self.access_token.
158
159 Args:
160 do_request: A function matching httplib2.Http.request's signature.
161 """
162 token_uri = (
163 'http://metadata.google.internal/computeMetadata/v1/instance/'
164 'service-accounts/%s/token') % self.__service_account_name
165 extra_headers = {'X-Google-Metadata-Request': 'True'}
166 request = urllib2.Request(token_uri, headers=extra_headers)
167 try:
168 content = urllib2.urlopen(request).read()
169 except urllib2.URLError as e:
170 raise exceptions.CommunicationError(
171 'Could not reach metadata service: %s' % e.reason)
172 try:
173 credential_info = json.loads(content)
174 except ValueError:
175 raise exceptions.CredentialsError(
176 'Invalid credentials response: uri %s' % token_uri)
177
178 self.access_token = credential_info['access_token']
179
180
181 # TODO: Currently, we can't even *load*
182 # `oauth2client.appengine` without being on appengine, because of how
183 # it handles imports. Fix that by splitting that module into
184 # GAE-specific and GAE-independent bits, and guarding imports.
185 class GaeAssertionCredentials(oauth2client.client.AssertionCredentials):
186 """Assertion credentials for Google App Engine apps."""
187
188 def __init__(self, scopes, **kwds):
189 if not util.DetectGae():
190 raise exceptions.ResourceUnavailableError(
191 'GCE credentials requested outside a GCE instance')
192 self._scopes = list(util.NormalizeScopes(scopes))
193 super(GaeAssertionCredentials, self).__init__(None, **kwds)
194
195 @classmethod
196 def Get(cls, *args, **kwds):
197 try:
198 return cls(*args, **kwds)
199 except exceptions.Error:
200 return None
201
202 @classmethod
203 def from_json(cls, json_data): # pylint: disable=g-bad-name
204 data = json.loads(json_data)
205 return GaeAssertionCredentials(data['_scopes'])
206
207 def _refresh(self, _): # pylint: disable=g-bad-name
208 """Refresh self.access_token.
209
210 Args:
211 _: (ignored) A function matching httplib2.Http.request's signature.
212 """
213 # pylint: disable=g-import-not-at-top
214 from google.appengine.api import app_identity
215 try:
216 token, _ = app_identity.get_access_token(self._scopes)
217 except app_identity.Error as e:
218 raise exceptions.CredentialsError(str(e))
219 self.access_token = token
220
221
222 # TODO: Switch this from taking a path to taking a stream.
223 def CredentialsFromFile(path, client_info):
224 """Read credentials from a file."""
225 credential_store = oauth2client.multistore_file.get_credential_storage(
226 path,
227 client_info['client_id'],
228 client_info['user_agent'],
229 client_info['scope'])
230 credentials = credential_store.get()
231 if credentials is None or credentials.invalid:
232 print 'Generating new OAuth credentials ...'
233 while True:
234 # If authorization fails, we want to retry, rather than let this
235 # cascade up and get caught elsewhere. If users want out of the
236 # retry loop, they can ^C.
237 try:
238 flow = oauth2client.client.OAuth2WebServerFlow(**client_info)
239 flow.redirect_uri = oauth2client.client.OOB_CALLBACK_URN
240 authorize_url = flow.step1_get_authorize_url()
241 print 'Go to the following link in your browser:'
242 print
243 print ' ' + authorize_url
244 print
245 code = raw_input('Enter verification code: ').strip()
246 credential = flow.step2_exchange(code)
247 credential_store.put(credential)
248 credential.set_store(credential_store)
249 break
250 except (oauth2client.client.FlowExchangeError, SystemExit) as e:
251 # Here SystemExit is "no credential at all", and the
252 # FlowExchangeError is "invalid" -- usually because you reused
253 # a token.
254 print 'Invalid authorization: %s' % (e,)
255 except httplib2.HttpLib2Error as e:
256 print 'Communication error: %s' % (e,)
257 raise exceptions.CredentialsError(
258 'Communication error creating credentials: %s' % e)
259 return credentials
OLDNEW
« no previous file with comments | « gslib/third_party/storage_apitools/base_api.py ('k') | gslib/third_party/storage_apitools/encoding.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698