• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""Helper routines to facilitate use of oauth2_client."""
16
17from __future__ import absolute_import
18
19import json
20import os
21import sys
22import time
23import webbrowser
24
25from oauth2client.client import OAuth2WebServerFlow
26
27from gcs_oauth2_boto_plugin import oauth2_client
28
29CLIENT_ID = None
30CLIENT_SECRET = None
31
32GOOGLE_OAUTH2_PROVIDER_AUTHORIZATION_URI = (
33    'https://accounts.google.com/o/oauth2/auth')
34GOOGLE_OAUTH2_PROVIDER_TOKEN_URI = (
35    'https://accounts.google.com/o/oauth2/token')
36GOOGLE_OAUTH2_DEFAULT_FILE_PASSWORD = 'notasecret'
37
38OOB_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
39
40
41def OAuth2ClientFromBotoConfig(
42    config, cred_type=oauth2_client.CredTypes.OAUTH2_USER_ACCOUNT):
43  token_cache = None
44  token_cache_type = config.get('OAuth2', 'token_cache', 'file_system')
45  if token_cache_type == 'file_system':
46    if config.has_option('OAuth2', 'token_cache_path_pattern'):
47      token_cache = oauth2_client.FileSystemTokenCache(
48          path_pattern=config.get('OAuth2', 'token_cache_path_pattern'))
49    else:
50      token_cache = oauth2_client.FileSystemTokenCache()
51  elif token_cache_type == 'in_memory':
52    token_cache = oauth2_client.InMemoryTokenCache()
53  else:
54    raise Exception(
55        "Invalid value for config option OAuth2/token_cache: %s" %
56        token_cache_type)
57
58  proxy_host = None
59  proxy_port = None
60  proxy_user = None
61  proxy_pass = None
62  if (config.has_option('Boto', 'proxy')
63      and config.has_option('Boto', 'proxy_port')):
64    proxy_host = config.get('Boto', 'proxy')
65    proxy_port = int(config.get('Boto', 'proxy_port'))
66    proxy_user = config.get('Boto', 'proxy_user', None)
67    proxy_pass = config.get('Boto', 'proxy_pass', None)
68
69  provider_authorization_uri = config.get(
70      'OAuth2', 'provider_authorization_uri',
71      GOOGLE_OAUTH2_PROVIDER_AUTHORIZATION_URI)
72  provider_token_uri = config.get(
73      'OAuth2', 'provider_token_uri', GOOGLE_OAUTH2_PROVIDER_TOKEN_URI)
74
75  if cred_type == oauth2_client.CredTypes.OAUTH2_SERVICE_ACCOUNT:
76    service_client_id = config.get('Credentials', 'gs_service_client_id', '')
77    private_key_filename = config.get('Credentials', 'gs_service_key_file', '')
78    with open(private_key_filename, 'rb') as private_key_file:
79      private_key = private_key_file.read()
80
81    json_key = None
82    try:
83      json_key = json.loads(private_key)
84    except ValueError:
85      pass
86    if json_key:
87      for json_entry in ('client_id', 'client_email', 'private_key_id',
88                         'private_key'):
89        if json_entry not in json_key:
90          raise Exception('The JSON private key file at %s '
91                          'did not contain the required entry: %s' %
92                          (private_key_filename, json_entry))
93
94      return oauth2_client.OAuth2JsonServiceAccountClient(
95          json_key['client_id'], json_key['client_email'],
96          json_key['private_key_id'], json_key['private_key'],
97          access_token_cache=token_cache, auth_uri=provider_authorization_uri,
98          token_uri=provider_token_uri,
99          disable_ssl_certificate_validation=not(config.getbool(
100              'Boto', 'https_validate_certificates', True)),
101          proxy_host=proxy_host, proxy_port=proxy_port,
102          proxy_user=proxy_user, proxy_pass=proxy_pass)
103    else:
104      key_file_pass = config.get('Credentials', 'gs_service_key_file_password',
105                                 GOOGLE_OAUTH2_DEFAULT_FILE_PASSWORD)
106
107      return oauth2_client.OAuth2ServiceAccountClient(
108          service_client_id, private_key, key_file_pass,
109          access_token_cache=token_cache, auth_uri=provider_authorization_uri,
110          token_uri=provider_token_uri,
111          disable_ssl_certificate_validation=not(config.getbool(
112              'Boto', 'https_validate_certificates', True)),
113          proxy_host=proxy_host, proxy_port=proxy_port,
114          proxy_user=proxy_user, proxy_pass=proxy_pass)
115
116  elif cred_type == oauth2_client.CredTypes.OAUTH2_USER_ACCOUNT:
117    client_id = config.get('OAuth2', 'client_id',
118                           os.environ.get('OAUTH2_CLIENT_ID', CLIENT_ID))
119    if not client_id:
120      raise Exception(
121          'client_id for your application obtained from '
122          'https://console.developers.google.com must be set in a boto config '
123          'or with OAUTH2_CLIENT_ID environment variable or with '
124          'gcs_oauth2_boto_plugin.SetFallbackClientIdAndSecret function.')
125
126    client_secret = config.get('OAuth2', 'client_secret',
127                               os.environ.get('OAUTH2_CLIENT_SECRET',
128                                              CLIENT_SECRET))
129    ca_certs_file=config.get_value('Boto', 'ca_certificates_file')
130    if ca_certs_file == 'system':
131      ca_certs_file = None
132
133    if not client_secret:
134      raise Exception(
135          'client_secret for your application obtained from '
136          'https://console.developers.google.com must be set in a boto config '
137          'or with OAUTH2_CLIENT_SECRET environment variable or with '
138          'gcs_oauth2_boto_plugin.SetFallbackClientIdAndSecret function.')
139    return oauth2_client.OAuth2UserAccountClient(
140        provider_token_uri, client_id, client_secret,
141        config.get('Credentials', 'gs_oauth2_refresh_token'),
142        auth_uri=provider_authorization_uri, access_token_cache=token_cache,
143        disable_ssl_certificate_validation=not(config.getbool(
144            'Boto', 'https_validate_certificates', True)),
145        proxy_host=proxy_host, proxy_port=proxy_port,
146        proxy_user=proxy_user, proxy_pass=proxy_pass,
147        ca_certs_file=ca_certs_file)
148  else:
149    raise Exception('You have attempted to create an OAuth2 client without '
150                    'setting up OAuth2 credentials.')
151
152
153def OAuth2ApprovalFlow(client, scopes, launch_browser=False):
154  flow = OAuth2WebServerFlow(
155      client.client_id, client.client_secret, scopes, auth_uri=client.auth_uri,
156      token_uri=client.token_uri, redirect_uri=OOB_REDIRECT_URI)
157  approval_url = flow.step1_get_authorize_url()
158
159  if launch_browser:
160    sys.stdout.write(
161        'Attempting to launch a browser with the OAuth2 approval dialog at '
162        'URL: %s\n\n'
163        '[Note: due to a Python bug, you may see a spurious error message '
164        '"object is not\ncallable [...] in [...] Popen.__del__" which can be '
165        'ignored.]\n\n' % approval_url)
166  else:
167    sys.stdout.write(
168        'Please navigate your browser to the following URL:\n%s\n' %
169        approval_url)
170
171  sys.stdout.write(
172      'In your browser you should see a page that requests you to authorize '
173      'access to Google Cloud Platform APIs and Services on your behalf. '
174      'After you approve, an authorization code will be displayed.\n\n')
175  if (launch_browser and
176      not webbrowser.open(approval_url, new=1, autoraise=True)):
177    sys.stdout.write(
178        'Launching browser appears to have failed; please navigate a browser '
179        'to the following URL:\n%s\n' % approval_url)
180  # Short delay; webbrowser.open on linux insists on printing out a message
181  # which we don't want to run into the prompt for the auth code.
182  time.sleep(2)
183  code = raw_input('Enter the authorization code: ')
184  credentials = flow.step2_exchange(code, http=client.CreateHttpRequest())
185  return credentials.refresh_token
186
187
188def SetFallbackClientIdAndSecret(client_id, client_secret):
189  global CLIENT_ID
190  global CLIENT_SECRET
191
192  CLIENT_ID = client_id
193  CLIENT_SECRET = client_secret
194
195
196def SetLock(lock):
197  oauth2_client.token_exchange_lock = lock
198
199