From 92c300923c1d9c8e2bb9619cc74278ab7ed74675 Mon Sep 17 00:00:00 2001 From: "pgervais@chromium.org" Date: Tue, 15 Apr 2014 00:30:37 +0000 Subject: [PATCH] Added POST capability to oauth Rietveld BUG=319446 Review URL: https://codereview.chromium.org/236093002 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@263750 0039d316-1c4b-4281-b951-d872f2087c98 --- presubmit_support.py | 33 ++++++++++++++++++++++++++++++--- rietveld.py | 27 ++++++++++++++++++--------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/presubmit_support.py b/presubmit_support.py index ae56b068b..f39d8212e 100755 --- a/presubmit_support.py +++ b/presubmit_support.py @@ -833,7 +833,7 @@ class Change(object): def SetDescriptionText(self, description): """Sets the full description text (including tags) to |description|. - + Also updates the list of tags.""" self._full_description = description @@ -1018,7 +1018,7 @@ class GetTrySlavesExecuter(object): @staticmethod def ExecPresubmitScript(script_text, presubmit_path, project, change): """Executes GetPreferredTrySlaves() from a single presubmit script. - + This will soon be deprecated and replaced by GetPreferredTryMasters(). Args: @@ -1541,25 +1541,52 @@ def Main(argv): parser.add_option("--rietveld_password", help=optparse.SUPPRESS_HELP) parser.add_option("--rietveld_fetch", action='store_true', default=False, help=optparse.SUPPRESS_HELP) + # These are for OAuth2 authentication for bots. See also apply_issue.py + parser.add_option("--rietveld_email_file", help=optparse.SUPPRESS_HELP) + parser.add_option("--rietveld_private_key_file", help=optparse.SUPPRESS_HELP) + parser.add_option("--trybot-json", help="Output trybot information to the file specified.") options, args = parser.parse_args(argv) + if options.verbose >= 2: logging.basicConfig(level=logging.DEBUG) elif options.verbose: logging.basicConfig(level=logging.INFO) else: logging.basicConfig(level=logging.ERROR) + + if options.rietveld_email and options.rietveld_email_file: + parser.error("Only one of --rietveld_email or --rietveld_email_file " + "can be passed to this program.") + if options.rietveld_private_key_file and options.rietveld_password: + parser.error("Only one of --rietveld_private_key_file or " + "--rietveld_password can be passed to this program.") + if options.rietveld_email_file: + with open(options.rietveld_email_file, "rb") as f: + options.rietveld_email = f.read().strip() + change_class, files = load_files(options, args) if not change_class: parser.error('For unversioned directory, is not optional.') logging.info('Found %d file(s).' % len(files)) + rietveld_obj = None if options.rietveld_url: - rietveld_obj = rietveld.CachingRietveld( + # The empty password is permitted: '' is not None. + if options.rietveld_password is not None: + rietveld_obj = rietveld.CachingRietveld( options.rietveld_url, options.rietveld_email, options.rietveld_password) + elif options.rietveld_private_key_file: + rietveld_obj = rietveld.JwtOAuth2Rietveld( + options.rietveld_url, + options.rietveld_email, + options.rietveld_private_key_file) + else: + parser.error("No password or secret key has been provided for " + "Rietveld. Unable to connect.") if options.rietveld_fetch: assert options.issue props = rietveld_obj.get_issue_properties(options.issue, False) diff --git a/rietveld.py b/rietveld.py index ba2a27d87..e296181ad 100644 --- a/rietveld.py +++ b/rietveld.py @@ -444,7 +444,7 @@ class Rietveld(object): class OAuthRpcServer(object): def __init__(self, host, - client_id, + client_email, client_private_key, private_key_password='notasecret', user_agent=None, @@ -452,7 +452,7 @@ class OAuthRpcServer(object): extra_headers=None): """Wrapper around httplib2.Http() that handles authentication. - client_id: client id for service account + client_email: email associated with the service account client_private_key: encrypted private key, as a string private_key_password: password used to decrypt the private key """ @@ -475,12 +475,12 @@ class OAuthRpcServer(object): self.extra_headers = extra_headers or {} if not oa2client.HAS_OPENSSL: - logging.error("Support for OpenSSL hasn't been found, " + logging.error("No support for OpenSSL has been found, " "OAuth2 support requires it.") logging.error("Installing pyopenssl will probably solve this issue.") raise RuntimeError('No OpenSSL support') creds = oa2client.SignedJwtAssertionCredentials( - client_id, + client_email, client_private_key, 'https://www.googleapis.com/auth/userinfo.email', private_key_password=private_key_password, @@ -513,7 +513,6 @@ class OAuthRpcServer(object): if payload is not None: method = 'POST' headers['Content-Type'] = content_type - raise NotImplementedError('POST requests are not yet supported.') prev_timeout = self._http.timeout try: @@ -528,7 +527,9 @@ class OAuthRpcServer(object): method=method, body=payload, headers=headers) - if not ret[0]['content-location'].startswith(self.host): + + if (method == 'GET' + and not ret[0]['content-location'].startswith(self.host)): upload.logging.warning('Redirection to host %s detected: ' 'login may have failed/expired.' % urlparse.urlparse( @@ -549,18 +550,26 @@ class JwtOAuth2Rietveld(Rietveld): # pylint: disable=W0231 def __init__(self, url, - client_id, + client_email, client_private_key_file, private_key_password=None, extra_headers=None): + + # These attributes are accessed by commit queue. Keep them. + self.email = client_email + self.private_key_file = client_private_key_file + if private_key_password is None: # '' means 'empty password' private_key_password = 'notasecret' self.url = url.rstrip('/') + bot_url = self.url + '/bots' + with open(client_private_key_file, 'rb') as f: client_private_key = f.read() - self.rpc_server = OAuthRpcServer(url, - client_id, + logging.info('Using OAuth login: %s' % client_email) + self.rpc_server = OAuthRpcServer(bot_url, + client_email, client_private_key, private_key_password=private_key_password, extra_headers=extra_headers or {})