Package oauth2client :: Module tools |
[hide private]
[frames] | no frames]

# Source Code for Module oauth2client.tools

      1  # Copyright (C) 2013 Google Inc.
      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  """Command-line tools for authenticating via OAuth 2.0
     16
     17  Do the OAuth 2.0 Web Server dance for a command line application. Stores the
     18  generated credentials in a common file that is used by other example apps in
     19  the same directory.
     20  """
     21
     22  __author__ = 'jcgregorio@google.com (Joe Gregorio)'
     23  __all__ = ['argparser', 'run_flow', 'run', 'message_if_missing']
     24
     25
     26  import BaseHTTPServer
     27  import argparse
     28  import httplib2
     29  import logging
     30  import os
     31  import socket
     32  import sys
     33  import webbrowser
     34
     35  from oauth2client import client
     36  from oauth2client import file
     37  from oauth2client import util
     38
     39  try:
     40    from urlparse import parse_qsl
     41  except ImportError:
     42    from cgi import parse_qsl
     43
     44  _CLIENT_SECRETS_MESSAGE = """WARNING: Please configure OAuth 2.0
     45
     46  To make this sample run you will need to populate the client_secrets.json file
     47  found at:
     48
     49     %s
     50
     51  with information from the APIs Console <https://code.google.com/apis/console>.
     52
     53  """
     54
     55  # run_parser is an ArgumentParser that contains command-line options expected
     56  # by tools.run(). Pass it in as part of the 'parents' argument to your own
     57  # ArgumentParser.
     58  argparser = argparse.ArgumentParser(add_help=False)
     59  argparser.add_argument('--auth_host_name', default='localhost',
     60                          help='Hostname when running a local web server.')
     61  argparser.add_argument('--noauth_local_webserver', action='store_true',
     62                          default=False, help='Do not run a local web server.')
     63  argparser.add_argument('--auth_host_port', default=[8080, 8090], type=int,
     64                          nargs='*', help='Port web server should listen on.')
     65  argparser.add_argument('--logging_level', default='ERROR',
     66                          choices=['DEBUG', 'INFO', 'WARNING', 'ERROR',
     67                                   'CRITICAL'],
     68                          help='Set the logging level of detail.')
     69
     70
     71 -class ClientRedirectServer(BaseHTTPServer.HTTPServer):
     72    """A server to handle OAuth 2.0 redirects back to localhost.
     73
     74    Waits for a single request and parses the query parameters
     75    into query_params and then stops serving.
     76    """
     77    query_params = {}
     78
     79
     80 -class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
     81    """A handler for OAuth 2.0 redirects back to localhost.
     82
     83    Waits for a single request and parses the query parameters
     84    into the servers query_params and then stops serving.
     85    """
     86
     87 -  def do_GET(s):
     88      """Handle a GET request.
     89
     90      Parses the query parameters and prints a message
     91      if the flow has completed. Note that we can't detect
     92      if an error occurred.
     93      """
     94      s.send_response(200)
     95      s.send_header("Content-type", "text/html")
     96      s.end_headers()
     97      query = s.path.split('?', 1)[-1]
     98      query = dict(parse_qsl(query))
     99      s.server.query_params = query
    100      s.wfile.write("<html><head><title>Authentication Status</title></head>")
    101      s.wfile.write("<body><p>The authentication flow has completed.</p>")
    102      s.wfile.write("</body></html>")
    103
    104 -  def log_message(self, format, *args):
    105      """Do not log messages to stdout while running as command line program."""
    106      pass
    107
    108
    109  @util.positional(3)
    110 -def run_flow(flow, storage, flags, http=None):
    111    """Core code for a command-line application.
    112
    113    The run() function is called from your application and runs through all the
    114    steps to obtain credentials. It takes a Flow argument and attempts to open an
    115    authorization server page in the user's default web browser. The server asks
    116    the user to grant your application access to the user's data. If the user
    117    grants access, the run() function returns new credentials. The new credentials
    118    are also stored in the Storage argument, which updates the file associated
    119    with the Storage object.
    120
    121    It presumes it is run from a command-line application and supports the
    122    following flags:
    123
    124      --auth_host_name: Host name to use when running a local web server
    125        to handle redirects during OAuth authorization.
    126        (default: 'localhost')
    127
    128      --auth_host_port: Port to use when running a local web server to handle
    129        redirects during OAuth authorization.;
    130        repeat this option to specify a list of values
    131        (default: '[8080, 8090]')
    132        (an integer)
    133
    134      --[no]auth_local_webserver: Run a local web server to handle redirects
    135        during OAuth authorization.
    136        (default: 'true')
    137
    138    The tools module defines an ArgumentParser the already contains the flag
    139    definitions that run() requires. You can pass that ArgumentParser to your
    140    ArgumentParser constructor:
    141
    142      parser = argparse.ArgumentParser(description=__doc__,
    143          formatter_class=argparse.RawDescriptionHelpFormatter,
    144          parents=[tools.run_parser])
    145      flags = parser.parse_args(argv)
    146
    147    Args:
    148      flow: Flow, an OAuth 2.0 Flow to step through.
    149      storage: Storage, a Storage to store the credential in.
    150      flags: argparse.ArgumentParser, the command-line flags.
    151      http: An instance of httplib2.Http.request
    152           or something that acts like it.
    153
    154    Returns:
    155      Credentials, the obtained credential.
    156    """
    157    logging.getLogger().setLevel(getattr(logging, flags.logging_level))
    158    if not flags.noauth_local_webserver:
    159      success = False
    160      port_number = 0
    161      for port in flags.auth_host_port:
    162        port_number = port
    163        try:
    164          httpd = ClientRedirectServer((flags.auth_host_name, port),
    165                                       ClientRedirectHandler)
    166        except socket.error, e:
    167          pass
    168        else:
    169          success = True
    170          break
    171      flags.noauth_local_webserver = not success
    172      if not success:
    173        print 'Failed to start a local webserver listening on either port 8080'
    174        print 'or port 9090. Please check your firewall settings and locally'
    175        print 'running programs that may be blocking or using those ports.'
    176        print
    177        print 'Falling back to --noauth_local_webserver and continuing with',
    178        print 'authorization.'
    179        print
    180
    181    if not flags.noauth_local_webserver:
    182      oauth_callback = 'http://%s:%s/' % (flags.auth_host_name, port_number)
    183    else:
    184      oauth_callback = client.OOB_CALLBACK_URN
    185    flow.redirect_uri = oauth_callback
    186    authorize_url = flow.step1_get_authorize_url()
    187
    188    if not flags.noauth_local_webserver:
    189      webbrowser.open(authorize_url, new=1, autoraise=True)
    190      print 'Your browser has been opened to visit:'
    191      print
    192      print '    ' + authorize_url
    193      print
    194      print 'If your browser is on a different machine then exit and re-run this'
    195      print 'application with the command-line parameter '
    196      print
    197      print '  --noauth_local_webserver'
    198      print
    199    else:
    200      print 'Go to the following link in your browser:'
    201      print
    202      print '    ' + authorize_url
    203      print
    204
    205    code = None
    206    if not flags.noauth_local_webserver:
    207      httpd.handle_request()
    208      if 'error' in httpd.query_params:
    209        sys.exit('Authentication request was rejected.')
    210      if 'code' in httpd.query_params:
    211        code = httpd.query_params['code']
    212      else:
    213        print 'Failed to find "code" in the query parameters of the redirect.'
    214        sys.exit('Try running with --noauth_local_webserver.')
    215    else:
    216      code = raw_input('Enter verification code: ').strip()
    217
    218    try:
    219      credential = flow.step2_exchange(code, http=http)
    220    except client.FlowExchangeError, e:
    221      sys.exit('Authentication has failed: %s' % e)
    222
    223    storage.put(credential)
    224    credential.set_store(storage)
    225    print 'Authentication successful.'
    226
    227    return credential
    228
    229
    230 -def message_if_missing(filename):
    231    """Helpful message to display if the CLIENT_SECRETS file is missing."""
    232
    233    return _CLIENT_SECRETS_MESSAGE % filename
    234
    235  try:
    236    from old_run import run
    237    from old_run import FLAGS
    238  except ImportError:
    239 -  def run(*args, **kwargs):
    240      raise NotImplementedError(
    241          'The gflags library must be installed to use tools.run(). '
    242          'Please install gflags or preferrably switch to using '
    243          'tools.run_flow().')
    244