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

Side by Side Diff: third_party/google-endpoints/apitools/scripts/oauth2l.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months 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
OLDNEW
(Empty)
1 #
2 # Copyright 2015 Google Inc.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """Command-line utility for fetching/inspecting credentials.
17
18 oauth2l (pronounced "oauthtool") is a small utility for fetching
19 credentials, or inspecting existing credentials. Here we demonstrate
20 some sample use:
21
22 $ oauth2l fetch userinfo.email bigquery compute
23 Fetched credentials of type:
24 oauth2client.client.OAuth2Credentials
25 Access token:
26 ya29.abcdefghijklmnopqrstuvwxyz123yessirree
27 $ oauth2l header userinfo.email
28 Authorization: Bearer ya29.zyxwvutsrqpnmolkjihgfedcba
29 $ oauth2l validate thisisnotatoken
30 <exit status: 1>
31 $ oauth2l validate ya29.zyxwvutsrqpnmolkjihgfedcba
32 $ oauth2l scopes ya29.abcdefghijklmnopqrstuvwxyz123yessirree
33 https://www.googleapis.com/auth/bigquery
34 https://www.googleapis.com/auth/compute
35 https://www.googleapis.com/auth/userinfo.email
36
37 The `header` command is designed to be easy to use with `curl`:
38
39 $ curl "$(oauth2l header bigquery)" \
40 'https://www.googleapis.com/bigquery/v2/projects'
41
42 The token can also be printed in other formats, for easy chaining
43 into other programs:
44
45 $ oauth2l fetch -f json_compact userinfo.email
46 <one-line JSON object with credential information>
47 $ oauth2l fetch -f bare drive
48 ya29.suchT0kenManyCredentialsW0Wokyougetthepoint
49
50 """
51
52 import httplib
53 import json
54 import logging
55 import os
56 import pkgutil
57 import sys
58 import textwrap
59
60 import gflags as flags
61 from google.apputils import appcommands
62 import oauth2client.client
63
64 import apitools.base.py as apitools_base
65 from apitools.base.py import cli as apitools_cli
66
67 FLAGS = flags.FLAGS
68 # We could use a generated client here, but it's used for precisely
69 # one URL, with one parameter and no worries about URL encoding. Let's
70 # go with simple.
71 _OAUTH2_TOKENINFO_TEMPLATE = (
72 'https://www.googleapis.com/oauth2/v2/tokeninfo'
73 '?access_token={access_token}'
74 )
75
76
77 flags.DEFINE_string(
78 'client_secrets', '',
79 'If specified, use the client ID/secret from the named '
80 'file, which should be a client_secrets.json file as downloaded '
81 'from the Developer Console.')
82 flags.DEFINE_string(
83 'credentials_filename', '',
84 '(optional) Filename for fetching/storing credentials.')
85 flags.DEFINE_string(
86 'service_account_json_keyfile', '',
87 'Filename for a JSON service account key downloaded from the Developer '
88 'Console.')
89
90
91 def GetDefaultClientInfo():
92 client_secrets = json.loads(pkgutil.get_data(
93 'apitools.data', 'apitools_client_secrets.json'))['installed']
94 return {
95 'client_id': client_secrets['client_id'],
96 'client_secret': client_secrets['client_secret'],
97 'user_agent': 'apitools/0.2 oauth2l/0.1',
98 }
99
100
101 def GetClientInfoFromFlags():
102 """Fetch client info from FLAGS."""
103 if FLAGS.client_secrets:
104 client_secrets_path = os.path.expanduser(FLAGS.client_secrets)
105 if not os.path.exists(client_secrets_path):
106 raise ValueError('Cannot find file: %s' % FLAGS.client_secrets)
107 with open(client_secrets_path) as client_secrets_file:
108 client_secrets = json.load(client_secrets_file)
109 if 'installed' not in client_secrets:
110 raise ValueError('Provided client ID must be for an installed app')
111 client_secrets = client_secrets['installed']
112 return {
113 'client_id': client_secrets['client_id'],
114 'client_secret': client_secrets['client_secret'],
115 'user_agent': 'apitools/0.2 oauth2l/0.1',
116 }
117 else:
118 return GetDefaultClientInfo()
119
120
121 def _ExpandScopes(scopes):
122 scope_prefix = 'https://www.googleapis.com/auth/'
123 return [s if s.startswith('https://') else scope_prefix + s
124 for s in scopes]
125
126
127 def _PrettyJson(data):
128 return json.dumps(data, sort_keys=True, indent=4, separators=(',', ': '))
129
130
131 def _CompactJson(data):
132 return json.dumps(data, sort_keys=True, separators=(',', ':'))
133
134
135 def _Format(fmt, credentials):
136 """Format credentials according to fmt."""
137 if fmt == 'bare':
138 return credentials.access_token
139 elif fmt == 'header':
140 return 'Authorization: Bearer %s' % credentials.access_token
141 elif fmt == 'json':
142 return _PrettyJson(json.loads(credentials.to_json()))
143 elif fmt == 'json_compact':
144 return _CompactJson(json.loads(credentials.to_json()))
145 elif fmt == 'pretty':
146 format_str = textwrap.dedent('\n'.join([
147 'Fetched credentials of type:',
148 ' {credentials_type.__module__}.{credentials_type.__name__}',
149 'Access token:',
150 ' {credentials.access_token}',
151 ]))
152 return format_str.format(credentials=credentials,
153 credentials_type=type(credentials))
154 raise ValueError('Unknown format: {}'.format(fmt))
155
156 _FORMATS = set(('bare', 'header', 'json', 'json_compact', 'pretty'))
157
158
159 def _GetTokenScopes(access_token):
160 """Return the list of valid scopes for the given token as a list."""
161 url = _OAUTH2_TOKENINFO_TEMPLATE.format(access_token=access_token)
162 response = apitools_base.MakeRequest(
163 apitools_base.GetHttp(), apitools_base.Request(url))
164 if response.status_code not in [httplib.OK, httplib.BAD_REQUEST]:
165 raise apitools_base.HttpError.FromResponse(response)
166 if response.status_code == httplib.BAD_REQUEST:
167 return []
168 return json.loads(response.content)['scope'].split(' ')
169
170
171 def _ValidateToken(access_token):
172 """Return True iff the provided access token is valid."""
173 return bool(_GetTokenScopes(access_token))
174
175
176 def FetchCredentials(scopes, client_info=None, credentials_filename=None):
177 """Fetch a credential for the given client_info and scopes."""
178 client_info = client_info or GetClientInfoFromFlags()
179 scopes = _ExpandScopes(scopes)
180 if not scopes:
181 raise ValueError('No scopes provided')
182 credentials_filename = credentials_filename or FLAGS.credentials_filename
183 # TODO(craigcitro): Remove this logging nonsense once we quiet the
184 # spurious logging in oauth2client.
185 old_level = logging.getLogger().level
186 logging.getLogger().setLevel(logging.ERROR)
187 credentials = apitools_base.GetCredentials(
188 'oauth2l', scopes, credentials_filename=credentials_filename,
189 service_account_json_keyfile=FLAGS.service_account_json_keyfile,
190 oauth2client_args='', **client_info)
191 logging.getLogger().setLevel(old_level)
192 if not _ValidateToken(credentials.access_token):
193 credentials.refresh(apitools_base.GetHttp())
194 return credentials
195
196
197 class _Email(apitools_cli.NewCmd):
198
199 """Get user email."""
200
201 usage = 'email <access_token>'
202
203 def RunWithArgs(self, access_token):
204 """Print the email address for this token, if possible."""
205 userinfo = apitools_base.GetUserinfo(
206 oauth2client.client.AccessTokenCredentials(access_token,
207 'oauth2l/1.0'))
208 user_email = userinfo.get('email')
209 if user_email:
210 print user_email
211
212
213 class _Fetch(apitools_cli.NewCmd):
214
215 """Fetch credentials."""
216
217 usage = 'fetch <scope> [<scope> ...]'
218
219 def __init__(self, name, flag_values):
220 super(_Fetch, self).__init__(name, flag_values)
221 flags.DEFINE_enum(
222 'credentials_format', 'pretty', sorted(_FORMATS),
223 'Output format for token.',
224 short_name='f', flag_values=flag_values)
225
226 def RunWithArgs(self, *scopes):
227 """Fetch a valid access token and display it."""
228 credentials = FetchCredentials(scopes)
229 print _Format(FLAGS.credentials_format.lower(), credentials)
230
231
232 class _Header(apitools_cli.NewCmd):
233
234 """Print credentials for a header."""
235
236 usage = 'header <scope> [<scope> ...]'
237
238 def RunWithArgs(self, *scopes):
239 """Fetch a valid access token and display it formatted for a header."""
240 print _Format('header', FetchCredentials(scopes))
241
242
243 class _Scopes(apitools_cli.NewCmd):
244
245 """Get the list of scopes for a token."""
246
247 usage = 'scopes <access_token>'
248
249 def RunWithArgs(self, access_token):
250 """Print the list of scopes for a valid token."""
251 scopes = _GetTokenScopes(access_token)
252 if not scopes:
253 return 1
254 for scope in sorted(scopes):
255 print scope
256
257
258 class _Userinfo(apitools_cli.NewCmd):
259
260 """Get userinfo."""
261
262 usage = 'userinfo <access_token>'
263
264 def __init__(self, name, flag_values):
265 super(_Userinfo, self).__init__(name, flag_values)
266 flags.DEFINE_enum(
267 'format', 'json', sorted(('json', 'json_compact')),
268 'Output format for userinfo.',
269 short_name='f', flag_values=flag_values)
270
271 def RunWithArgs(self, access_token):
272 """Print the userinfo for this token (if we have the right scopes)."""
273 userinfo = apitools_base.GetUserinfo(
274 oauth2client.client.AccessTokenCredentials(access_token,
275 'oauth2l/1.0'))
276 if FLAGS.format == 'json':
277 print _PrettyJson(userinfo)
278 else:
279 print _CompactJson(userinfo)
280
281
282 class _Validate(apitools_cli.NewCmd):
283
284 """Validate a token."""
285
286 usage = 'validate <access_token>'
287
288 def RunWithArgs(self, access_token):
289 """Validate an access token. Exits with 0 if valid, 1 otherwise."""
290 return 1 - (_ValidateToken(access_token))
291
292
293 def run_main(): # pylint:disable=invalid-name
294 """Function to be used as setuptools script entry point."""
295 # Put the flags for this module somewhere the flags module will look
296 # for them.
297
298 # pylint:disable=protected-access
299 new_name = flags._GetMainModule()
300 sys.modules[new_name] = sys.modules['__main__']
301 for flag in FLAGS.FlagsByModuleDict().get(__name__, []):
302 FLAGS._RegisterFlagByModule(new_name, flag)
303 for key_flag in FLAGS.KeyFlagsByModuleDict().get(__name__, []):
304 FLAGS._RegisterKeyFlagForModule(new_name, key_flag)
305 # pylint:enable=protected-access
306
307 # Now set __main__ appropriately so that appcommands will be
308 # happy.
309 sys.modules['__main__'] = sys.modules[__name__]
310 appcommands.Run()
311 sys.modules['__main__'] = sys.modules.pop(new_name)
312
313
314 def main(unused_argv):
315 appcommands.AddCmd('email', _Email)
316 appcommands.AddCmd('fetch', _Fetch)
317 appcommands.AddCmd('header', _Header)
318 appcommands.AddCmd('scopes', _Scopes)
319 appcommands.AddCmd('userinfo', _Userinfo)
320 appcommands.AddCmd('validate', _Validate)
321
322
323 if __name__ == '__main__':
324 appcommands.Run()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698