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) |
| 429 auth_attempted = False |
473 try: | 430 try: |
474 tries = 0 | 431 tries = 0 |
475 while True: | 432 while True: |
476 tries += 1 | 433 tries += 1 |
477 args = dict(kwargs) | 434 args = dict(kwargs) |
478 url = "%s%s" % (self.host, request_path) | 435 url = "%s%s" % (self.host, request_path) |
479 if args: | 436 if args: |
480 url += "?" + urllib.urlencode(args) | 437 url += "?" + urllib.urlencode(args) |
481 req = self._CreateRequest(url=url, data=payload) | 438 req = self._CreateRequest(url=url, data=payload) |
482 req.add_header("Content-Type", content_type) | 439 req.add_header("Content-Type", content_type) |
483 if extra_headers: | 440 if extra_headers: |
484 for header, value in extra_headers.items(): | 441 for header, value in extra_headers.items(): |
485 req.add_header(header, value) | 442 req.add_header(header, value) |
486 try: | 443 try: |
487 f = self.opener.open(req, timeout=70) | 444 f = self.opener.open(req, timeout=70) |
488 response = f.read() | 445 response = f.read() |
489 f.close() | 446 f.close() |
490 return response | 447 return response |
491 except urllib2.HTTPError, e: | 448 except urllib2.HTTPError, e: |
492 if tries > 3: | 449 if tries > 3: |
493 raise | 450 raise |
494 elif e.code == 401 or e.code == 302: | 451 elif e.code in (302, 401, 403): |
495 if not self.auth_function: | 452 if not self.auth_function: |
496 raise | 453 raise |
497 self._Authenticate() | 454 # Already tried force refresh, didn't help -> give up with error. |
| 455 if auth_attempted: |
| 456 raise auth.AuthenticationError( |
| 457 'Access to %s is denied (server returned HTTP %d).' |
| 458 % (self.host, e.code)) |
| 459 self._Authenticate(force_refresh=True) |
| 460 auth_attempted = True |
498 elif e.code == 301: | 461 elif e.code == 301: |
499 # Handle permanent redirect manually. | 462 # Handle permanent redirect manually. |
500 url = e.info()["location"] | 463 url = e.info()["location"] |
501 url_loc = urlparse.urlparse(url) | 464 url_loc = urlparse.urlparse(url) |
502 self.host = '%s://%s' % (url_loc[0], url_loc[1]) | 465 self.host = '%s://%s' % (url_loc[0], url_loc[1]) |
503 elif e.code >= 500: | 466 elif e.code >= 500: |
504 # TODO: We should error out on a 500, but the server is too flaky | 467 # TODO: We should error out on a 500, but the server is too flaky |
505 # for that at the moment. | 468 # for that at the moment. |
506 StatusUpdate('Upload got a 500 response: %d' % e.code) | 469 StatusUpdate('Upload got a 500 response: %d' % e.code) |
507 else: | 470 else: |
508 raise | 471 raise |
509 finally: | 472 finally: |
510 socket.setdefaulttimeout(old_timeout) | 473 socket.setdefaulttimeout(old_timeout) |
511 | 474 |
512 | 475 |
513 class HttpRpcServer(AbstractRpcServer): | 476 class HttpRpcServer(AbstractRpcServer): |
514 """Provides a simplified RPC-style interface for HTTP requests.""" | 477 """Provides a simplified RPC-style interface for HTTP requests.""" |
515 | 478 |
516 def _Authenticate(self): | 479 def _Authenticate(self, force_refresh): |
517 """Save the cookie jar after authentication.""" | 480 """Save the cookie jar after authentication.""" |
518 if isinstance(self.auth_function, OAuth2Creds): | 481 if isinstance(self.auth_function, auth.Authenticator): |
519 access_token = self.auth_function() | 482 try: |
520 if access_token is not None: | 483 access_token = self.auth_function.get_access_token(force_refresh) |
521 self.extra_headers['Authorization'] = 'OAuth %s' % (access_token,) | 484 except auth.LoginRequiredError: |
522 self.authenticated = True | 485 # Attempt to make unauthenticated request first if there's no cached |
| 486 # credentials. HttpRpcServer calls __Authenticate(force_refresh=True) |
| 487 # again if unauthenticated request doesn't work. |
| 488 if not force_refresh: |
| 489 return |
| 490 raise |
| 491 self.extra_headers['Authorization'] = 'Bearer %s' % ( |
| 492 access_token.token,) |
523 else: | 493 else: |
524 super(HttpRpcServer, self)._Authenticate() | 494 super(HttpRpcServer, self)._Authenticate(force_refresh) |
525 if self.save_cookies: | 495 if self.save_cookies: |
526 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) | 496 StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) |
527 self.cookie_jar.save() | 497 self.cookie_jar.save() |
528 | 498 |
529 def _GetOpener(self): | 499 def _GetOpener(self): |
530 """Returns an OpenerDirector that supports cookies and ignores redirects. | 500 """Returns an OpenerDirector that supports cookies and ignores redirects. |
531 | 501 |
532 Returns: | 502 Returns: |
533 A urllib2.OpenerDirector object. | 503 A urllib2.OpenerDirector object. |
534 """ | 504 """ |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
707 metavar="P4_CHANGELIST", default=None, | 677 metavar="P4_CHANGELIST", default=None, |
708 help=("Perforce changelist id")) | 678 help=("Perforce changelist id")) |
709 group.add_option("--p4_client", action="store", dest="p4_client", | 679 group.add_option("--p4_client", action="store", dest="p4_client", |
710 metavar="P4_CLIENT", default=None, | 680 metavar="P4_CLIENT", default=None, |
711 help=("Perforce client/workspace")) | 681 help=("Perforce client/workspace")) |
712 group.add_option("--p4_user", action="store", dest="p4_user", | 682 group.add_option("--p4_user", action="store", dest="p4_user", |
713 metavar="P4_USER", default=None, | 683 metavar="P4_USER", default=None, |
714 help=("Perforce user")) | 684 help=("Perforce user")) |
715 | 685 |
716 | 686 |
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): | 687 class KeyringCreds(object): |
862 def __init__(self, server, host, email): | 688 def __init__(self, server, host, email): |
863 self.server = server | 689 self.server = server |
864 # Explicitly cast host to str to work around bug in old versions of Keyring | 690 # 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, | 691 # (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 | 692 # some modern linuxes (such as Ubuntu 12.04) still bundle a version with |
867 # the bug. | 693 # the bug. |
868 self.host = str(host) | 694 self.host = str(host) |
869 self.email = email | 695 self.email = email |
870 self.accounts_seen = set() | 696 self.accounts_seen = set() |
(...skipping 25 matching lines...) Expand all Loading... |
896 else: | 722 else: |
897 password = getpass.getpass("Password for %s: " % email) | 723 password = getpass.getpass("Password for %s: " % email) |
898 if keyring: | 724 if keyring: |
899 answer = raw_input("Store password in system keyring?(y/N) ").strip() | 725 answer = raw_input("Store password in system keyring?(y/N) ").strip() |
900 if answer == "y": | 726 if answer == "y": |
901 keyring.set_password(self.host, email, password) | 727 keyring.set_password(self.host, email, password) |
902 self.accounts_seen.add(email) | 728 self.accounts_seen.add(email) |
903 return (email, password) | 729 return (email, password) |
904 | 730 |
905 | 731 |
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): | 732 def GetRpcServer(server, auth_config=None, email=None): |
921 """Returns an instance of an AbstractRpcServer. | 733 """Returns an instance of an AbstractRpcServer. |
922 | 734 |
923 Args: | 735 Args: |
924 server: String containing the review server URL. | 736 server: String containing the review server URL. |
925 auth_config: auth.AuthConfig tuple with OAuth2 configuration. | 737 auth_config: auth.AuthConfig tuple with OAuth2 configuration. |
926 email: String containing user's email address [deprecated]. | 738 email: String containing user's email address [deprecated]. |
927 | 739 |
928 Returns: | 740 Returns: |
929 A new HttpRpcServer, on which RPC calls can be made. | 741 A new HttpRpcServer, on which RPC calls can be made. |
930 """ | 742 """ |
931 # If email is given as an empty string or no auth config is passed, then | 743 # 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 | 744 # assume we want to make requests that do not need authentication. Bypass |
933 # authentication by setting the auth_function to None. | 745 # authentication by setting the auth_function to None. |
934 if email == '' or not auth_config: | 746 if email == '' or not auth_config: |
935 return HttpRpcServer(server, None) | 747 return HttpRpcServer(server, None) |
936 | 748 |
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. | 749 # If this is the dev_appserver, use fake authentication. |
941 host = server.lower() | 750 host = server.lower() |
942 if re.match(r'(http://)?localhost([:/]|$)', host): | 751 if re.match(r'(http://)?localhost([:/]|$)', host): |
943 if email is None: | 752 if email is None: |
944 email = "test@example.com" | 753 email = "test@example.com" |
945 LOGGER.info("Using debug user %s. Override with --email" % email) | 754 LOGGER.info("Using debug user %s. Override with --email" % email) |
946 server = HttpRpcServer( | 755 server = HttpRpcServer( |
947 server, | 756 server, |
948 lambda: (email, "password"), | 757 lambda: (email, "password"), |
949 extra_headers={"Cookie": | 758 extra_headers={"Cookie": |
950 'dev_appserver_login="%s:False"' % email}, | 759 'dev_appserver_login="%s:False"' % email}, |
951 save_cookies=auth_config.save_cookies, | 760 save_cookies=auth_config.save_cookies, |
952 account_type=AUTH_ACCOUNT_TYPE) | 761 account_type=AUTH_ACCOUNT_TYPE) |
953 # Don't try to talk to ClientLogin. | 762 # Don't try to talk to ClientLogin. |
954 server.authenticated = True | 763 server.authenticated = True |
955 return server | 764 return server |
956 | 765 |
| 766 if auth_config.use_oauth2: |
| 767 auth_func = auth.get_authenticator_for_host(server, auth_config) |
| 768 else: |
| 769 auth_func = KeyringCreds(server, host, email).GetUserCredentials |
| 770 |
957 return HttpRpcServer( | 771 return HttpRpcServer( |
958 server, | 772 server, |
959 KeyringCreds(server, host, email).GetUserCredentials, | 773 auth_func, |
960 save_cookies=auth_config.save_cookies, | 774 save_cookies=auth_config.save_cookies, |
961 account_type=AUTH_ACCOUNT_TYPE) | 775 account_type=AUTH_ACCOUNT_TYPE) |
962 | 776 |
963 | 777 |
964 def EncodeMultipartFormData(fields, files): | 778 def EncodeMultipartFormData(fields, files): |
965 """Encode form fields for multipart/form-data. | 779 """Encode form fields for multipart/form-data. |
966 | 780 |
967 Args: | 781 Args: |
968 fields: A sequence of (name, value) elements for regular form fields. | 782 fields: A sequence of (name, value) elements for regular form fields. |
969 files: A sequence of (name, filename, value) elements for data to be | 783 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(): | 2520 def main(): |
2707 try: | 2521 try: |
2708 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" | 2522 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" |
2709 "%(lineno)s %(message)s ")) | 2523 "%(lineno)s %(message)s ")) |
2710 os.environ['LC_ALL'] = 'C' | 2524 os.environ['LC_ALL'] = 'C' |
2711 RealMain(sys.argv) | 2525 RealMain(sys.argv) |
2712 except KeyboardInterrupt: | 2526 except KeyboardInterrupt: |
2713 print | 2527 print |
2714 StatusUpdate("Interrupted.") | 2528 StatusUpdate("Interrupted.") |
2715 sys.exit(1) | 2529 sys.exit(1) |
| 2530 except auth.AuthenticationError as e: |
| 2531 print >> sys.stderr, e |
| 2532 sys.exit(1) |
2716 | 2533 |
2717 | 2534 |
2718 if __name__ == "__main__": | 2535 if __name__ == "__main__": |
2719 main() | 2536 main() |
OLD | NEW |