| OLD | NEW |
| 1 # coding: utf-8 | 1 # coding: utf-8 |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Defines class Rietveld to easily access a rietveld instance. | 5 """Defines class Rietveld to easily access a rietveld instance. |
| 6 | 6 |
| 7 Security implications: | 7 Security implications: |
| 8 | 8 |
| 9 The following hypothesis are made: | 9 The following hypothesis are made: |
| 10 - Rietveld enforces: | 10 - Rietveld enforces: |
| (...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 437 finally: | 437 finally: |
| 438 upload.ErrorExit = old_error_exit | 438 upload.ErrorExit = old_error_exit |
| 439 | 439 |
| 440 # DEPRECATED. | 440 # DEPRECATED. |
| 441 Send = get | 441 Send = get |
| 442 | 442 |
| 443 | 443 |
| 444 class OAuthRpcServer(object): | 444 class OAuthRpcServer(object): |
| 445 def __init__(self, | 445 def __init__(self, |
| 446 host, | 446 host, |
| 447 client_id, | 447 client_email, |
| 448 client_private_key, | 448 client_private_key, |
| 449 private_key_password='notasecret', | 449 private_key_password='notasecret', |
| 450 user_agent=None, | 450 user_agent=None, |
| 451 timeout=None, | 451 timeout=None, |
| 452 extra_headers=None): | 452 extra_headers=None): |
| 453 """Wrapper around httplib2.Http() that handles authentication. | 453 """Wrapper around httplib2.Http() that handles authentication. |
| 454 | 454 |
| 455 client_id: client id for service account | 455 client_email: email associated with the service account |
| 456 client_private_key: encrypted private key, as a string | 456 client_private_key: encrypted private key, as a string |
| 457 private_key_password: password used to decrypt the private key | 457 private_key_password: password used to decrypt the private key |
| 458 """ | 458 """ |
| 459 | 459 |
| 460 # Enforce https | 460 # Enforce https |
| 461 host_parts = urlparse.urlparse(host) | 461 host_parts = urlparse.urlparse(host) |
| 462 | 462 |
| 463 if host_parts.scheme == 'https': # fine | 463 if host_parts.scheme == 'https': # fine |
| 464 self.host = host | 464 self.host = host |
| 465 elif host_parts.scheme == 'http': | 465 elif host_parts.scheme == 'http': |
| 466 upload.logging.warning('Changing protocol to https') | 466 upload.logging.warning('Changing protocol to https') |
| 467 self.host = 'https' + host[4:] | 467 self.host = 'https' + host[4:] |
| 468 else: | 468 else: |
| 469 msg = 'Invalid url provided: %s' % host | 469 msg = 'Invalid url provided: %s' % host |
| 470 upload.logging.error(msg) | 470 upload.logging.error(msg) |
| 471 raise ValueError(msg) | 471 raise ValueError(msg) |
| 472 | 472 |
| 473 self.host = self.host.rstrip('/') | 473 self.host = self.host.rstrip('/') |
| 474 | 474 |
| 475 self.extra_headers = extra_headers or {} | 475 self.extra_headers = extra_headers or {} |
| 476 | 476 |
| 477 if not oa2client.HAS_OPENSSL: | 477 if not oa2client.HAS_OPENSSL: |
| 478 logging.error("Support for OpenSSL hasn't been found, " | 478 logging.error("No support for OpenSSL has been found, " |
| 479 "OAuth2 support requires it.") | 479 "OAuth2 support requires it.") |
| 480 logging.error("Installing pyopenssl will probably solve this issue.") | 480 logging.error("Installing pyopenssl will probably solve this issue.") |
| 481 raise RuntimeError('No OpenSSL support') | 481 raise RuntimeError('No OpenSSL support') |
| 482 creds = oa2client.SignedJwtAssertionCredentials( | 482 creds = oa2client.SignedJwtAssertionCredentials( |
| 483 client_id, | 483 client_email, |
| 484 client_private_key, | 484 client_private_key, |
| 485 'https://www.googleapis.com/auth/userinfo.email', | 485 'https://www.googleapis.com/auth/userinfo.email', |
| 486 private_key_password=private_key_password, | 486 private_key_password=private_key_password, |
| 487 user_agent=user_agent) | 487 user_agent=user_agent) |
| 488 | 488 |
| 489 self._http = creds.authorize(httplib2.Http(timeout=timeout)) | 489 self._http = creds.authorize(httplib2.Http(timeout=timeout)) |
| 490 | 490 |
| 491 def Send(self, | 491 def Send(self, |
| 492 request_path, | 492 request_path, |
| 493 payload=None, | 493 payload=None, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 506 """ | 506 """ |
| 507 # This method signature should match upload.py:AbstractRpcServer.Send() | 507 # This method signature should match upload.py:AbstractRpcServer.Send() |
| 508 method = 'GET' | 508 method = 'GET' |
| 509 | 509 |
| 510 headers = self.extra_headers.copy() | 510 headers = self.extra_headers.copy() |
| 511 headers.update(extra_headers or {}) | 511 headers.update(extra_headers or {}) |
| 512 | 512 |
| 513 if payload is not None: | 513 if payload is not None: |
| 514 method = 'POST' | 514 method = 'POST' |
| 515 headers['Content-Type'] = content_type | 515 headers['Content-Type'] = content_type |
| 516 raise NotImplementedError('POST requests are not yet supported.') | |
| 517 | 516 |
| 518 prev_timeout = self._http.timeout | 517 prev_timeout = self._http.timeout |
| 519 try: | 518 try: |
| 520 if timeout: | 519 if timeout: |
| 521 self._http.timeout = timeout | 520 self._http.timeout = timeout |
| 522 # TODO(pgervais) implement some kind of retry mechanism (see upload.py). | 521 # TODO(pgervais) implement some kind of retry mechanism (see upload.py). |
| 523 url = self.host + request_path | 522 url = self.host + request_path |
| 524 if kwargs: | 523 if kwargs: |
| 525 url += "?" + urllib.urlencode(kwargs) | 524 url += "?" + urllib.urlencode(kwargs) |
| 526 | 525 |
| 527 ret = self._http.request(url, | 526 ret = self._http.request(url, |
| 528 method=method, | 527 method=method, |
| 529 body=payload, | 528 body=payload, |
| 530 headers=headers) | 529 headers=headers) |
| 531 if not ret[0]['content-location'].startswith(self.host): | 530 |
| 531 if (method == 'GET' |
| 532 and not ret[0]['content-location'].startswith(self.host)): |
| 532 upload.logging.warning('Redirection to host %s detected: ' | 533 upload.logging.warning('Redirection to host %s detected: ' |
| 533 'login may have failed/expired.' | 534 'login may have failed/expired.' |
| 534 % urlparse.urlparse( | 535 % urlparse.urlparse( |
| 535 ret[0]['content-location']).netloc) | 536 ret[0]['content-location']).netloc) |
| 536 return ret[1] | 537 return ret[1] |
| 537 | 538 |
| 538 finally: | 539 finally: |
| 539 self._http.timeout = prev_timeout | 540 self._http.timeout = prev_timeout |
| 540 | 541 |
| 541 | 542 |
| 542 class JwtOAuth2Rietveld(Rietveld): | 543 class JwtOAuth2Rietveld(Rietveld): |
| 543 """Access to Rietveld using OAuth authentication. | 544 """Access to Rietveld using OAuth authentication. |
| 544 | 545 |
| 545 This class is supposed to be used only by bots, since this kind of | 546 This class is supposed to be used only by bots, since this kind of |
| 546 access is restricted to service accounts. | 547 access is restricted to service accounts. |
| 547 """ | 548 """ |
| 548 # The parent__init__ is not called on purpose. | 549 # The parent__init__ is not called on purpose. |
| 549 # pylint: disable=W0231 | 550 # pylint: disable=W0231 |
| 550 def __init__(self, | 551 def __init__(self, |
| 551 url, | 552 url, |
| 552 client_id, | 553 client_email, |
| 553 client_private_key_file, | 554 client_private_key_file, |
| 554 private_key_password=None, | 555 private_key_password=None, |
| 555 extra_headers=None): | 556 extra_headers=None): |
| 557 |
| 558 # These attributes are accessed by commit queue. Keep them. |
| 559 self.email = client_email |
| 560 self.private_key_file = client_private_key_file |
| 561 |
| 556 if private_key_password is None: # '' means 'empty password' | 562 if private_key_password is None: # '' means 'empty password' |
| 557 private_key_password = 'notasecret' | 563 private_key_password = 'notasecret' |
| 558 | 564 |
| 559 self.url = url.rstrip('/') | 565 self.url = url.rstrip('/') |
| 566 bot_url = self.url + '/bots' |
| 567 |
| 560 with open(client_private_key_file, 'rb') as f: | 568 with open(client_private_key_file, 'rb') as f: |
| 561 client_private_key = f.read() | 569 client_private_key = f.read() |
| 562 self.rpc_server = OAuthRpcServer(url, | 570 logging.info('Using OAuth login: %s' % client_email) |
| 563 client_id, | 571 self.rpc_server = OAuthRpcServer(bot_url, |
| 572 client_email, |
| 564 client_private_key, | 573 client_private_key, |
| 565 private_key_password=private_key_password, | 574 private_key_password=private_key_password, |
| 566 extra_headers=extra_headers or {}) | 575 extra_headers=extra_headers or {}) |
| 567 self._xsrf_token = None | 576 self._xsrf_token = None |
| 568 self._xsrf_token_time = None | 577 self._xsrf_token_time = None |
| 569 | 578 |
| 570 | 579 |
| 571 class CachingRietveld(Rietveld): | 580 class CachingRietveld(Rietveld): |
| 572 """Caches the common queries. | 581 """Caches the common queries. |
| 573 | 582 |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 693 def trigger_try_jobs( # pylint:disable=R0201 | 702 def trigger_try_jobs( # pylint:disable=R0201 |
| 694 self, issue, patchset, reason, clobber, revision, builders_and_tests, | 703 self, issue, patchset, reason, clobber, revision, builders_and_tests, |
| 695 master=None): | 704 master=None): |
| 696 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 705 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 697 (builders_and_tests, issue)) | 706 (builders_and_tests, issue)) |
| 698 | 707 |
| 699 def trigger_distributed_try_jobs( # pylint:disable=R0201 | 708 def trigger_distributed_try_jobs( # pylint:disable=R0201 |
| 700 self, issue, patchset, reason, clobber, revision, masters): | 709 self, issue, patchset, reason, clobber, revision, masters): |
| 701 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 710 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 702 (masters, issue)) | 711 (masters, issue)) |
| OLD | NEW |