| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # coding: utf-8 | 2 # coding: utf-8 |
| 3 # | 3 # |
| 4 # Copyright 2007 Google Inc. | 4 # Copyright 2007 Google Inc. |
| 5 # | 5 # |
| 6 # Licensed under the Apache License, Version 2.0 (the "License"); | 6 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 # you may not use this file except in compliance with the License. | 7 # you may not use this file except in compliance with the License. |
| 8 # You may obtain a copy of the License at | 8 # You may obtain a copy of the License at |
| 9 # | 9 # |
| 10 # http://www.apache.org/licenses/LICENSE-2.0 | 10 # http://www.apache.org/licenses/LICENSE-2.0 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 Subversion | 27 Subversion |
| 28 Perforce | 28 Perforce |
| 29 CVS | 29 CVS |
| 30 | 30 |
| 31 It is important for Git/Mercurial users to specify a tree/node/branch to diff | 31 It is important for Git/Mercurial users to specify a tree/node/branch to diff |
| 32 against by using the '--rev' option. | 32 against by using the '--rev' option. |
| 33 """ | 33 """ |
| 34 # This code is derived from appcfg.py in the App Engine SDK (open source), | 34 # This code is derived from appcfg.py in the App Engine SDK (open source), |
| 35 # and from ASPN recipe #146306. | 35 # and from ASPN recipe #146306. |
| 36 | 36 |
| 37 import BaseHTTPServer | |
| 38 import ConfigParser | 37 import ConfigParser |
| 39 import cookielib | 38 import cookielib |
| 40 import errno | 39 import errno |
| 41 import fnmatch | 40 import fnmatch |
| 42 import getpass | 41 import getpass |
| 43 import logging | 42 import logging |
| 44 import marshal | 43 import marshal |
| 45 import mimetypes | 44 import mimetypes |
| 46 import optparse | 45 import optparse |
| 47 import os | 46 import os |
| 48 import re | 47 import re |
| 49 import socket | 48 import socket |
| 50 import subprocess | 49 import subprocess |
| 51 import sys | 50 import sys |
| 52 import urllib | 51 import urllib |
| 53 import urllib2 | 52 import urllib2 |
| 54 import urlparse | 53 import urlparse |
| 55 import webbrowser | |
| 56 | 54 |
| 57 from multiprocessing.pool import ThreadPool | 55 from multiprocessing.pool import ThreadPool |
| 58 | 56 |
| 59 # The md5 module was deprecated in Python 2.5. | 57 # The md5 module was deprecated in Python 2.5. |
| 60 try: | 58 try: |
| 61 from hashlib import md5 | 59 from hashlib import md5 |
| 62 except ImportError: | 60 except ImportError: |
| 63 from md5 import md5 | 61 from md5 import md5 |
| 64 | 62 |
| 65 try: | 63 try: |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 ] | 117 ] |
| 120 | 118 |
| 121 | 119 |
| 122 VCS_SHORT_NAMES = [] # hg, svn, ... | 120 VCS_SHORT_NAMES = [] # hg, svn, ... |
| 123 VCS_ABBREVIATIONS = {} # alias: name, ... | 121 VCS_ABBREVIATIONS = {} # alias: name, ... |
| 124 for vcs in VCS: | 122 for vcs in VCS: |
| 125 VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len)) | 123 VCS_SHORT_NAMES.append(min(vcs['aliases'], key=len)) |
| 126 VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases']) | 124 VCS_ABBREVIATIONS.update((alias, vcs['name']) for alias in vcs['aliases']) |
| 127 | 125 |
| 128 | 126 |
| 129 # OAuth 2.0-Related Constants | |
| 130 LOCALHOST_IP = '127.0.0.1' | |
| 131 DEFAULT_OAUTH2_PORT = 8001 | |
| 132 ACCESS_TOKEN_PARAM = 'access_token' | |
| 133 ERROR_PARAM = 'error' | |
| 134 OAUTH_DEFAULT_ERROR_MESSAGE = 'OAuth 2.0 error occurred.' | |
| 135 OAUTH_PATH = '/get-access-token' | |
| 136 OAUTH_PATH_PORT_TEMPLATE = OAUTH_PATH + '?port=%(port)d' | |
| 137 AUTH_HANDLER_RESPONSE = """\ | |
| 138 <html> | |
| 139 <head> | |
| 140 <title>Authentication Status</title> | |
| 141 <script> | |
| 142 window.onload = function() { | |
| 143 window.close(); | |
| 144 } | |
| 145 </script> | |
| 146 </head> | |
| 147 <body> | |
| 148 <p>The authentication flow has completed.</p> | |
| 149 </body> | |
| 150 </html> | |
| 151 """ | |
| 152 # Borrowed from google-api-python-client | |
| 153 OPEN_LOCAL_MESSAGE_TEMPLATE = """\ | |
| 154 Your browser has been opened to visit: | |
| 155 | |
| 156 %s | |
| 157 | |
| 158 If your browser is on a different machine then exit and re-run | |
| 159 upload.py with the command-line parameter | |
| 160 | |
| 161 --no_oauth2_webbrowser | |
| 162 """ | |
| 163 NO_OPEN_LOCAL_MESSAGE_TEMPLATE = """\ | |
| 164 Go to the following link in your browser: | |
| 165 | |
| 166 %s | |
| 167 | |
| 168 and copy the access token. | |
| 169 """ | |
| 170 | |
| 171 # The result of parsing Subversion's [auto-props] setting. | 127 # The result of parsing Subversion's [auto-props] setting. |
| 172 svn_auto_props_map = None | 128 svn_auto_props_map = None |
| 173 | 129 |
| 174 def GetEmail(prompt): | 130 def GetEmail(prompt): |
| 175 """Prompts the user for their email address and returns it. | 131 """Prompts the user for their email address and returns it. |
| 176 | 132 |
| 177 The last used email address is saved to a file and offered up as a suggestion | 133 The last used email address is saved to a file and offered up as a suggestion |
| 178 to the user. If the user presses enter without typing in anything the last | 134 to the user. If the user presses enter without typing in anything the last |
| 179 used email address is used. If the user enters a new address, it is saved | 135 used email address is used. If the user enters a new address, it is saved |
| 180 for next time we prompt. | 136 for next time we prompt. |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 try: | 310 try: |
| 355 response = self.opener.open(req) | 311 response = self.opener.open(req) |
| 356 except urllib2.HTTPError, e: | 312 except urllib2.HTTPError, e: |
| 357 response = e | 313 response = e |
| 358 if (response.code != 302 or | 314 if (response.code != 302 or |
| 359 response.info()["location"] != continue_location): | 315 response.info()["location"] != continue_location): |
| 360 raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, | 316 raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, |
| 361 response.headers, response.fp) | 317 response.headers, response.fp) |
| 362 self.authenticated = True | 318 self.authenticated = True |
| 363 | 319 |
| 364 def _Authenticate(self): | 320 def _Authenticate(self, force_refresh): |
| 365 """Authenticates the user. | 321 """Authenticates the user. |
| 366 | 322 |
| 367 The authentication process works as follows: | 323 The authentication process works as follows: |
| 368 1) We get a username and password from the user | 324 1) We get a username and password from the user |
| 369 2) We use ClientLogin to obtain an AUTH token for the user | 325 2) We use ClientLogin to obtain an AUTH token for the user |
| 370 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). | 326 (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). |
| 371 3) We pass the auth token to /_ah/login on the server to obtain an | 327 3) We pass the auth token to /_ah/login on the server to obtain an |
| 372 authentication cookie. If login was successful, it tries to redirect | 328 authentication cookie. If login was successful, it tries to redirect |
| 373 us to the URL we provided. | 329 us to the URL we provided. |
| 374 | 330 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 included in the request (string header names mapped to their values), | 415 included in the request (string header names mapped to their values), |
| 460 or None to not include any additional headers. | 416 or None to not include any additional headers. |
| 461 kwargs: Any keyword arguments are converted into query string parameters. | 417 kwargs: Any keyword arguments are converted into query string parameters. |
| 462 | 418 |
| 463 Returns: | 419 Returns: |
| 464 The response body, as a string. | 420 The response body, as a string. |
| 465 """ | 421 """ |
| 466 # TODO: Don't require authentication. Let the server say | 422 # TODO: Don't require authentication. Let the server say |
| 467 # whether it is necessary. | 423 # whether it is necessary. |
| 468 if not self.authenticated and self.auth_function: | 424 if not self.authenticated and self.auth_function: |
| 469 self._Authenticate() | 425 self._Authenticate(force_refresh=False) |
| 470 | 426 |
| 471 old_timeout = socket.getdefaulttimeout() | 427 old_timeout = socket.getdefaulttimeout() |
| 472 socket.setdefaulttimeout(timeout) | 428 socket.setdefaulttimeout(timeout) |
| 473 try: | 429 try: |
| 474 tries = 0 | 430 tries = 0 |
| 475 while True: | 431 while True: |
| 476 tries += 1 | 432 tries += 1 |
| 477 args = dict(kwargs) | 433 args = dict(kwargs) |
| 478 url = "%s%s" % (self.host, request_path) | 434 url = "%s%s" % (self.host, request_path) |
| 479 if args: | 435 if args: |
| 480 url += "?" + urllib.urlencode(args) | 436 url += "?" + urllib.urlencode(args) |
| 481 req = self._CreateRequest(url=url, data=payload) | 437 req = self._CreateRequest(url=url, data=payload) |
| 482 req.add_header("Content-Type", content_type) | 438 req.add_header("Content-Type", content_type) |
| 483 if extra_headers: | 439 if extra_headers: |
| 484 for header, value in extra_headers.items(): | 440 for header, value in extra_headers.items(): |
| 485 req.add_header(header, value) | 441 req.add_header(header, value) |
| 486 try: | 442 try: |
| 487 f = self.opener.open(req, timeout=70) | 443 f = self.opener.open(req, timeout=70) |
| 488 response = f.read() | 444 response = f.read() |
| 489 f.close() | 445 f.close() |
| 490 return response | 446 return response |
| 491 except urllib2.HTTPError, e: | 447 except urllib2.HTTPError, e: |
| 492 if tries > 3: | 448 if tries > 3: |
| 493 raise | 449 raise |
| 494 elif e.code == 401 or e.code == 302: | 450 elif e.code == 401 or e.code == 302: |
| 495 if not self.auth_function: | 451 if not self.auth_function: |
| 496 raise | 452 raise |
| 497 self._Authenticate() | 453 self._Authenticate(force_refresh=True) |
| 498 elif e.code == 301: | 454 elif e.code == 301: |
| 499 # Handle permanent redirect manually. | 455 # Handle permanent redirect manually. |
| 500 url = e.info()["location"] | 456 url = e.info()["location"] |
| 501 url_loc = urlparse.urlparse(url) | 457 url_loc = urlparse.urlparse(url) |
| 502 self.host = '%s://%s' % (url_loc[0], url_loc[1]) | 458 self.host = '%s://%s' % (url_loc[0], url_loc[1]) |
| 503 elif e.code >= 500: | 459 elif e.code >= 500: |
| 504 # TODO: We should error out on a 500, but the server is too flaky | 460 # TODO: We should error out on a 500, but the server is too flaky |
| 505 # for that at the moment. | 461 # for that at the moment. |
| 506 StatusUpdate('Upload got a 500 response: %d' % e.code) | 462 StatusUpdate('Upload got a 500 response: %d' % e.code) |
| 507 else: | 463 else: |
| 508 raise | 464 raise |
| 509 finally: | 465 finally: |
| 510 socket.setdefaulttimeout(old_timeout) | 466 socket.setdefaulttimeout(old_timeout) |
| 511 | 467 |
| 512 | 468 |
| 513 class HttpRpcServer(AbstractRpcServer): | 469 class HttpRpcServer(AbstractRpcServer): |
| 514 """Provides a simplified RPC-style interface for HTTP requests.""" | 470 """Provides a simplified RPC-style interface for HTTP requests.""" |
| 515 | 471 |
| 516 def _Authenticate(self): | 472 def _Authenticate(self, force_refresh): |
| 517 """Save the cookie jar after authentication.""" | 473 """Save the cookie jar after authentication.""" |
| 518 if isinstance(self.auth_function, OAuth2Creds): | 474 if isinstance(self.auth_function, auth.Authenticator): |
| 519 access_token = self.auth_function() | 475 try: |
| 520 if access_token is not None: | 476 access_token = self.auth_function.get_access_token(force_refresh) |
| 521 self.extra_headers['Authorization'] = 'OAuth %s' % (access_token,) | 477 except auth.LoginRequiredError: |
| 522 self.authenticated = True | 478 # Attempt to make unauthenticated request first if there's no cached |
| 479 # credentials. HttpRpcServer calls __Authenticate(force_refresh=True) |
| 480 # again if unauthenticated request doesn't work. |
| 481 if not force_refresh: |
| 482 return |
| 483 raise |
| 484 self.extra_headers['Authorization'] = 'Bearer %s' % ( |
| 485 access_token.token,) |
| 523 else: | 486 else: |
| 524 super(HttpRpcServer, self)._Authenticate() | 487 super(HttpRpcServer, self)._Authenticate(force_refresh) |
| 525 if self.save_cookies: | 488 if self.save_cookies: |
| 526 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) | 489 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) |
| 527 self.cookie_jar.save() | 490 self.cookie_jar.save() |
| 528 | 491 |
| 529 def _GetOpener(self): | 492 def _GetOpener(self): |
| 530 """Returns an OpenerDirector that supports cookies and ignores redirects. | 493 """Returns an OpenerDirector that supports cookies and ignores redirects. |
| 531 | 494 |
| 532 Returns: | 495 Returns: |
| 533 A urllib2.OpenerDirector object. | 496 A urllib2.OpenerDirector object. |
| 534 """ | 497 """ |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 707 metavar="P4_CHANGELIST", default=None, | 670 metavar="P4_CHANGELIST", default=None, |
| 708 help=("Perforce changelist id")) | 671 help=("Perforce changelist id")) |
| 709 group.add_option("--p4_client", action="store", dest="p4_client", | 672 group.add_option("--p4_client", action="store", dest="p4_client", |
| 710 metavar="P4_CLIENT", default=None, | 673 metavar="P4_CLIENT", default=None, |
| 711 help=("Perforce client/workspace")) | 674 help=("Perforce client/workspace")) |
| 712 group.add_option("--p4_user", action="store", dest="p4_user", | 675 group.add_option("--p4_user", action="store", dest="p4_user", |
| 713 metavar="P4_USER", default=None, | 676 metavar="P4_USER", default=None, |
| 714 help=("Perforce user")) | 677 help=("Perforce user")) |
| 715 | 678 |
| 716 | 679 |
| 717 # OAuth 2.0 Methods and Helpers | |
| 718 class ClientRedirectServer(BaseHTTPServer.HTTPServer): | |
| 719 """A server for redirects back to localhost from the associated server. | |
| 720 | |
| 721 Waits for a single request and parses the query parameters for an access token | |
| 722 or an error and then stops serving. | |
| 723 """ | |
| 724 access_token = None | |
| 725 error = None | |
| 726 | |
| 727 | |
| 728 class ClientRedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |
| 729 """A handler for redirects back to localhost from the associated server. | |
| 730 | |
| 731 Waits for a single request and parses the query parameters into the server's | |
| 732 access_token or error and then stops serving. | |
| 733 """ | |
| 734 | |
| 735 def SetResponseValue(self): | |
| 736 """Stores the access token or error from the request on the server. | |
| 737 | |
| 738 Will only do this if exactly one query parameter was passed in to the | |
| 739 request and that query parameter used 'access_token' or 'error' as the key. | |
| 740 """ | |
| 741 query_string = urlparse.urlparse(self.path).query | |
| 742 query_params = urlparse.parse_qs(query_string) | |
| 743 | |
| 744 if len(query_params) == 1: | |
| 745 if query_params.has_key(ACCESS_TOKEN_PARAM): | |
| 746 access_token_list = query_params[ACCESS_TOKEN_PARAM] | |
| 747 if len(access_token_list) == 1: | |
| 748 self.server.access_token = access_token_list[0] | |
| 749 else: | |
| 750 error_list = query_params.get(ERROR_PARAM, []) | |
| 751 if len(error_list) == 1: | |
| 752 self.server.error = error_list[0] | |
| 753 | |
| 754 def do_GET(self): | |
| 755 """Handle a GET request. | |
| 756 | |
| 757 Parses and saves the query parameters and prints a message that the server | |
| 758 has completed its lone task (handling a redirect). | |
| 759 | |
| 760 Note that we can't detect if an error occurred. | |
| 761 """ | |
| 762 self.send_response(200) | |
| 763 self.send_header('Content-type', 'text/html') | |
| 764 self.end_headers() | |
| 765 self.SetResponseValue() | |
| 766 self.wfile.write(AUTH_HANDLER_RESPONSE) | |
| 767 | |
| 768 def log_message(self, format, *args): | |
| 769 """Do not log messages to stdout while running as command line program.""" | |
| 770 pass | |
| 771 | |
| 772 | |
| 773 def OpenOAuth2ConsentPage(server=DEFAULT_REVIEW_SERVER, | |
| 774 port=DEFAULT_OAUTH2_PORT): | |
| 775 """Opens the OAuth 2.0 consent page or prints instructions how to. | |
| 776 | |
| 777 Uses the webbrowser module to open the OAuth server side page in a browser. | |
| 778 | |
| 779 Args: | |
| 780 server: String containing the review server URL. Defaults to | |
| 781 DEFAULT_REVIEW_SERVER. | |
| 782 port: Integer, the port where the localhost server receiving the redirect | |
| 783 is serving. Defaults to DEFAULT_OAUTH2_PORT. | |
| 784 | |
| 785 Returns: | |
| 786 A boolean indicating whether the page opened successfully. | |
| 787 """ | |
| 788 path = OAUTH_PATH_PORT_TEMPLATE % {'port': port} | |
| 789 parsed_url = urlparse.urlparse(server) | |
| 790 scheme = parsed_url[0] or 'https' | |
| 791 if scheme != 'https': | |
| 792 ErrorExit('Using OAuth requires a review server with SSL enabled.') | |
| 793 # If no scheme was given on command line the server address ends up in | |
| 794 # parsed_url.path otherwise in netloc. | |
| 795 host = parsed_url[1] or parsed_url[2] | |
| 796 page = '%s://%s%s' % (scheme, host, path) | |
| 797 page_opened = webbrowser.open(page, new=1, autoraise=True) | |
| 798 if page_opened: | |
| 799 print OPEN_LOCAL_MESSAGE_TEMPLATE % (page,) | |
| 800 return page_opened | |
| 801 | |
| 802 | |
| 803 def WaitForAccessToken(port=DEFAULT_OAUTH2_PORT): | |
| 804 """Spins up a simple HTTP Server to handle a single request. | |
| 805 | |
| 806 Intended to handle a single redirect from the production server after the | |
| 807 user authenticated via OAuth 2.0 with the server. | |
| 808 | |
| 809 Args: | |
| 810 port: Integer, the port where the localhost server receiving the redirect | |
| 811 is serving. Defaults to DEFAULT_OAUTH2_PORT. | |
| 812 | |
| 813 Returns: | |
| 814 The access token passed to the localhost server, or None if no access token | |
| 815 was passed. | |
| 816 """ | |
| 817 httpd = ClientRedirectServer((LOCALHOST_IP, port), ClientRedirectHandler) | |
| 818 # Wait to serve just one request before deferring control back | |
| 819 # to the caller of wait_for_refresh_token | |
| 820 httpd.handle_request() | |
| 821 if httpd.access_token is None: | |
| 822 ErrorExit(httpd.error or OAUTH_DEFAULT_ERROR_MESSAGE) | |
| 823 return httpd.access_token | |
| 824 | |
| 825 | |
| 826 def GetAccessToken(server=DEFAULT_REVIEW_SERVER, port=DEFAULT_OAUTH2_PORT, | |
| 827 open_local_webbrowser=True): | |
| 828 """Gets an Access Token for the current user. | |
| 829 | |
| 830 Args: | |
| 831 server: String containing the review server URL. Defaults to | |
| 832 DEFAULT_REVIEW_SERVER. | |
| 833 port: Integer, the port where the localhost server receiving the redirect | |
| 834 is serving. Defaults to DEFAULT_OAUTH2_PORT. | |
| 835 open_local_webbrowser: Boolean, defaults to True. If set, opens a page in | |
| 836 the user's browser. | |
| 837 | |
| 838 Returns: | |
| 839 A string access token that was sent to the local server. If the serving page | |
| 840 via WaitForAccessToken does not receive an access token, this method | |
| 841 returns None. | |
| 842 """ | |
| 843 access_token = None | |
| 844 if open_local_webbrowser: | |
| 845 page_opened = OpenOAuth2ConsentPage(server=server, port=port) | |
| 846 if page_opened: | |
| 847 try: | |
| 848 access_token = WaitForAccessToken(port=port) | |
| 849 except socket.error, e: | |
| 850 print 'Can\'t start local webserver. Socket Error: %s\n' % (e.strerror,) | |
| 851 | |
| 852 if access_token is None: | |
| 853 # TODO(dhermes): Offer to add to clipboard using xsel, xclip, pbcopy, etc. | |
| 854 page = 'https://%s%s' % (server, OAUTH_PATH) | |
| 855 print NO_OPEN_LOCAL_MESSAGE_TEMPLATE % (page,) | |
| 856 access_token = raw_input('Enter access token: ').strip() | |
| 857 | |
| 858 return access_token | |
| 859 | |
| 860 | |
| 861 class KeyringCreds(object): | 680 class KeyringCreds(object): |
| 862 def __init__(self, server, host, email): | 681 def __init__(self, server, host, email): |
| 863 self.server = server | 682 self.server = server |
| 864 # Explicitly cast host to str to work around bug in old versions of Keyring | 683 # Explicitly cast host to str to work around bug in old versions of Keyring |
| 865 # (versions before 0.10). Even though newer versions of Keyring fix this, | 684 # (versions before 0.10). Even though newer versions of Keyring fix this, |
| 866 # some modern linuxes (such as Ubuntu 12.04) still bundle a version with | 685 # some modern linuxes (such as Ubuntu 12.04) still bundle a version with |
| 867 # the bug. | 686 # the bug. |
| 868 self.host = str(host) | 687 self.host = str(host) |
| 869 self.email = email | 688 self.email = email |
| 870 self.accounts_seen = set() | 689 self.accounts_seen = set() |
| (...skipping 25 matching lines...) Expand all Loading... |
| 896 else: | 715 else: |
| 897 password = getpass.getpass("Password for %s: " % email) | 716 password = getpass.getpass("Password for %s: " % email) |
| 898 if keyring: | 717 if keyring: |
| 899 answer = raw_input("Store password in system keyring?(y/N) ").strip() | 718 answer = raw_input("Store password in system keyring?(y/N) ").strip() |
| 900 if answer == "y": | 719 if answer == "y": |
| 901 keyring.set_password(self.host, email, password) | 720 keyring.set_password(self.host, email, password) |
| 902 self.accounts_seen.add(email) | 721 self.accounts_seen.add(email) |
| 903 return (email, password) | 722 return (email, password) |
| 904 | 723 |
| 905 | 724 |
| 906 class OAuth2Creds(object): | |
| 907 """Simple object to hold server and port to be passed to GetAccessToken.""" | |
| 908 | |
| 909 def __init__(self, server, port, open_local_webbrowser=True): | |
| 910 self.server = server | |
| 911 self.port = port | |
| 912 self.open_local_webbrowser = open_local_webbrowser | |
| 913 | |
| 914 def __call__(self): | |
| 915 """Uses stored server and port to retrieve OAuth 2.0 access token.""" | |
| 916 return GetAccessToken(server=self.server, port=self.port, | |
| 917 open_local_webbrowser=self.open_local_webbrowser) | |
| 918 | |
| 919 | |
| 920 def GetRpcServer(server, auth_config=None, email=None): | 725 def GetRpcServer(server, auth_config=None, email=None): |
| 921 """Returns an instance of an AbstractRpcServer. | 726 """Returns an instance of an AbstractRpcServer. |
| 922 | 727 |
| 923 Args: | 728 Args: |
| 924 server: String containing the review server URL. | 729 server: String containing the review server URL. |
| 925 auth_config: auth.AuthConfig tuple with OAuth2 configuration. | 730 auth_config: auth.AuthConfig tuple with OAuth2 configuration. |
| 926 email: String containing user's email address [deprecated]. | 731 email: String containing user's email address [deprecated]. |
| 927 | 732 |
| 928 Returns: | 733 Returns: |
| 929 A new HttpRpcServer, on which RPC calls can be made. | 734 A new HttpRpcServer, on which RPC calls can be made. |
| 930 """ | 735 """ |
| 931 # If email is given as an empty string or no auth config is passed, then | 736 # If email is given as an empty string or no auth config is passed, then |
| 932 # assume we want to make requests that do not need authentication. Bypass | 737 # assume we want to make requests that do not need authentication. Bypass |
| 933 # authentication by setting the auth_function to None. | 738 # authentication by setting the auth_function to None. |
| 934 if email == '' or not auth_config: | 739 if email == '' or not auth_config: |
| 935 return HttpRpcServer(server, None) | 740 return HttpRpcServer(server, None) |
| 936 | 741 |
| 937 if auth_config.use_oauth2: | |
| 938 raise NotImplementedError('See https://crbug.com/356813') | |
| 939 | |
| 940 # If this is the dev_appserver, use fake authentication. | 742 # If this is the dev_appserver, use fake authentication. |
| 941 host = server.lower() | 743 host = server.lower() |
| 942 if re.match(r'(http://)?localhost([:/]|$)', host): | 744 if re.match(r'(http://)?localhost([:/]|$)', host): |
| 943 if email is None: | 745 if email is None: |
| 944 email = "test@example.com" | 746 email = "test@example.com" |
| 945 LOGGER.info("Using debug user %s. Override with --email" % email) | 747 LOGGER.info("Using debug user %s. Override with --email" % email) |
| 946 server = HttpRpcServer( | 748 server = HttpRpcServer( |
| 947 server, | 749 server, |
| 948 lambda: (email, "password"), | 750 lambda: (email, "password"), |
| 949 extra_headers={"Cookie": | 751 extra_headers={"Cookie": |
| 950 'dev_appserver_login="%s:False"' % email}, | 752 'dev_appserver_login="%s:False"' % email}, |
| 951 save_cookies=auth_config.save_cookies, | 753 save_cookies=auth_config.save_cookies, |
| 952 account_type=AUTH_ACCOUNT_TYPE) | 754 account_type=AUTH_ACCOUNT_TYPE) |
| 953 # Don't try to talk to ClientLogin. | 755 # Don't try to talk to ClientLogin. |
| 954 server.authenticated = True | 756 server.authenticated = True |
| 955 return server | 757 return server |
| 956 | 758 |
| 759 if auth_config.use_oauth2: |
| 760 auth_func = auth.get_authenticator_for_host(server, auth_config) |
| 761 else: |
| 762 auth_func = KeyringCreds(server, host, email).GetUserCredentials, |
| 763 |
| 957 return HttpRpcServer( | 764 return HttpRpcServer( |
| 958 server, | 765 server, |
| 959 KeyringCreds(server, host, email).GetUserCredentials, | 766 auth_func, |
| 960 save_cookies=auth_config.save_cookies, | 767 save_cookies=auth_config.save_cookies, |
| 961 account_type=AUTH_ACCOUNT_TYPE) | 768 account_type=AUTH_ACCOUNT_TYPE) |
| 962 | 769 |
| 963 | 770 |
| 964 def EncodeMultipartFormData(fields, files): | 771 def EncodeMultipartFormData(fields, files): |
| 965 """Encode form fields for multipart/form-data. | 772 """Encode form fields for multipart/form-data. |
| 966 | 773 |
| 967 Args: | 774 Args: |
| 968 fields: A sequence of (name, value) elements for regular form fields. | 775 fields: A sequence of (name, value) elements for regular form fields. |
| 969 files: A sequence of (name, filename, value) elements for data to be | 776 files: A sequence of (name, filename, value) elements for data to be |
| (...skipping 1736 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2706 def main(): | 2513 def main(): |
| 2707 try: | 2514 try: |
| 2708 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" | 2515 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" |
| 2709 "%(lineno)s %(message)s ")) | 2516 "%(lineno)s %(message)s ")) |
| 2710 os.environ['LC_ALL'] = 'C' | 2517 os.environ['LC_ALL'] = 'C' |
| 2711 RealMain(sys.argv) | 2518 RealMain(sys.argv) |
| 2712 except KeyboardInterrupt: | 2519 except KeyboardInterrupt: |
| 2713 print | 2520 print |
| 2714 StatusUpdate("Interrupted.") | 2521 StatusUpdate("Interrupted.") |
| 2715 sys.exit(1) | 2522 sys.exit(1) |
| 2523 except auth.AuthenticationError as e: |
| 2524 print >> sys.stderr, e |
| 2525 sys.exit(1) |
| 2716 | 2526 |
| 2717 | 2527 |
| 2718 if __name__ == "__main__": | 2528 if __name__ == "__main__": |
| 2719 main() | 2529 main() |
| OLD | NEW |