Chromium Code Reviews| 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 429 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 440 finally: | 440 finally: |
| 441 upload.ErrorExit = old_error_exit | 441 upload.ErrorExit = old_error_exit |
| 442 | 442 |
| 443 # DEPRECATED. | 443 # DEPRECATED. |
| 444 Send = get | 444 Send = get |
| 445 | 445 |
| 446 | 446 |
| 447 class OAuthRpcServer(object): | 447 class OAuthRpcServer(object): |
| 448 def __init__(self, | 448 def __init__(self, |
| 449 host, | 449 host, |
| 450 client_id, | 450 client_email, |
| 451 client_private_key, | 451 client_private_key, |
| 452 private_key_password='notasecret', | 452 private_key_password='notasecret', |
| 453 user_agent=None, | 453 user_agent=None, |
| 454 timeout=None, | 454 timeout=None, |
| 455 extra_headers=None): | 455 extra_headers=None): |
| 456 """Wrapper around httplib2.Http() that handles authentication. | 456 """Wrapper around httplib2.Http() that handles authentication. |
| 457 | 457 |
| 458 client_id: client id for service account | 458 client_email: email associated with the service account |
| 459 client_private_key: encrypted private key, as a string | 459 client_private_key: encrypted private key, as a string |
| 460 private_key_password: password used to decrypt the private key | 460 private_key_password: password used to decrypt the private key |
| 461 """ | 461 """ |
| 462 | 462 |
| 463 # Enforce https | 463 # Enforce https |
| 464 host_parts = urlparse.urlparse(host) | 464 host_parts = urlparse.urlparse(host) |
| 465 | 465 |
| 466 if host_parts.scheme == 'https': # fine | 466 if host_parts.scheme == 'https': # fine |
| 467 self.host = host | 467 self.host = host |
| 468 elif host_parts.scheme == 'http': | 468 elif host_parts.scheme == 'http': |
| 469 upload.logging.warning('Changing protocol to https') | 469 upload.logging.warning('Changing protocol to https') |
| 470 self.host = 'https' + host[4:] | 470 self.host = 'https' + host[4:] |
| 471 else: | 471 else: |
| 472 msg = 'Invalid url provided: %s' % host | 472 msg = 'Invalid url provided: %s' % host |
| 473 upload.logging.error(msg) | 473 upload.logging.error(msg) |
| 474 raise ValueError(msg) | 474 raise ValueError(msg) |
| 475 | 475 |
| 476 self.host = self.host.rstrip('/') | 476 self.host = self.host.rstrip('/') |
| 477 | 477 |
| 478 self.extra_headers = extra_headers or {} | 478 self.extra_headers = extra_headers or {} |
| 479 | 479 |
| 480 if not oa2client.HAS_OPENSSL: | 480 if not oa2client.HAS_OPENSSL: |
| 481 logging.error("Support for OpenSSL hasn't been found, " | 481 logging.error("No support for OpenSSL has been found, " |
| 482 "OAuth2 support requires it.") | 482 "OAuth2 support requires it.") |
| 483 logging.error("Installing pyopenssl will probably solve this issue.") | 483 logging.error("Installing pyopenssl will probably solve this issue.") |
| 484 raise RuntimeError('No OpenSSL support') | 484 raise RuntimeError('No OpenSSL support') |
| 485 creds = oa2client.SignedJwtAssertionCredentials( | 485 creds = oa2client.SignedJwtAssertionCredentials( |
| 486 client_id, | 486 client_email, |
| 487 client_private_key, | 487 client_private_key, |
| 488 'https://www.googleapis.com/auth/userinfo.email', | 488 'https://www.googleapis.com/auth/userinfo.email', |
| 489 private_key_password=private_key_password, | 489 private_key_password=private_key_password, |
| 490 user_agent=user_agent) | 490 user_agent=user_agent) |
| 491 | 491 |
| 492 self._http = creds.authorize(httplib2.Http(timeout=timeout)) | 492 self._http = creds.authorize(httplib2.Http(timeout=timeout)) |
| 493 | 493 |
| 494 def Send(self, | 494 def Send(self, |
| 495 request_path, | 495 request_path, |
| 496 payload=None, | 496 payload=None, |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 509 """ | 509 """ |
| 510 # This method signature should match upload.py:AbstractRpcServer.Send() | 510 # This method signature should match upload.py:AbstractRpcServer.Send() |
| 511 method = 'GET' | 511 method = 'GET' |
| 512 | 512 |
| 513 headers = self.extra_headers.copy() | 513 headers = self.extra_headers.copy() |
| 514 headers.update(extra_headers or {}) | 514 headers.update(extra_headers or {}) |
| 515 | 515 |
| 516 if payload is not None: | 516 if payload is not None: |
| 517 method = 'POST' | 517 method = 'POST' |
| 518 headers['Content-Type'] = content_type | 518 headers['Content-Type'] = content_type |
| 519 raise NotImplementedError('POST requests are not yet supported.') | 519 # raise NotImplementedError('POST requests are not yet supported.') |
|
Sergey Berezin
2014/04/14 20:59:42
Let's remove this comment.
pgervais
2014/04/14 21:28:05
Done.
| |
| 520 | 520 |
| 521 prev_timeout = self._http.timeout | 521 prev_timeout = self._http.timeout |
| 522 try: | 522 try: |
| 523 if timeout: | 523 if timeout: |
| 524 self._http.timeout = timeout | 524 self._http.timeout = timeout |
| 525 # TODO(pgervais) implement some kind of retry mechanism (see upload.py). | 525 # TODO(pgervais) implement some kind of retry mechanism (see upload.py). |
| 526 url = self.host + request_path | 526 url = self.host + request_path |
| 527 if kwargs: | 527 if kwargs: |
| 528 url += "?" + urllib.urlencode(kwargs) | 528 url += "?" + urllib.urlencode(kwargs) |
| 529 | 529 |
| 530 logging.info(url) | |
|
Sergey Berezin
2014/04/14 20:59:42
Please add a meaningful message to the log.
pgervais
2014/04/14 21:28:05
Removed it (was for testing, and forgotten)
| |
| 530 ret = self._http.request(url, | 531 ret = self._http.request(url, |
| 531 method=method, | 532 method=method, |
| 532 body=payload, | 533 body=payload, |
| 533 headers=headers) | 534 headers=headers) |
| 534 if not ret[0]['content-location'].startswith(self.host): | 535 |
| 536 if (method == 'GET' | |
| 537 and not ret[0]['content-location'].startswith(self.host)): | |
| 535 upload.logging.warning('Redirection to host %s detected: ' | 538 upload.logging.warning('Redirection to host %s detected: ' |
| 536 'login may have failed/expired.' | 539 'login may have failed/expired.' |
| 537 % urlparse.urlparse( | 540 % urlparse.urlparse( |
| 538 ret[0]['content-location']).netloc) | 541 ret[0]['content-location']).netloc) |
| 539 return ret[1] | 542 return ret[1] |
| 540 | 543 |
| 541 finally: | 544 finally: |
| 542 self._http.timeout = prev_timeout | 545 self._http.timeout = prev_timeout |
| 543 | 546 |
| 544 | 547 |
| 545 class JwtOAuth2Rietveld(Rietveld): | 548 class JwtOAuth2Rietveld(Rietveld): |
| 546 """Access to Rietveld using OAuth authentication. | 549 """Access to Rietveld using OAuth authentication. |
| 547 | 550 |
| 548 This class is supposed to be used only by bots, since this kind of | 551 This class is supposed to be used only by bots, since this kind of |
| 549 access is restricted to service accounts. | 552 access is restricted to service accounts. |
| 550 """ | 553 """ |
| 551 # The parent__init__ is not called on purpose. | 554 # The parent__init__ is not called on purpose. |
| 552 # pylint: disable=W0231 | 555 # pylint: disable=W0231 |
| 553 def __init__(self, | 556 def __init__(self, |
| 554 url, | 557 url, |
| 555 client_id, | 558 client_email, |
| 556 client_private_key_file, | 559 client_private_key_file, |
| 557 private_key_password=None, | 560 private_key_password=None, |
| 558 extra_headers=None): | 561 extra_headers=None): |
| 562 | |
| 563 # These attributes are accessed by commit queue. Keep them. | |
| 564 self.email = client_email | |
| 565 self.private_key_file = client_private_key_file | |
| 566 | |
| 559 if private_key_password is None: # '' means 'empty password' | 567 if private_key_password is None: # '' means 'empty password' |
| 560 private_key_password = 'notasecret' | 568 private_key_password = 'notasecret' |
| 561 | 569 |
| 562 self.url = url.rstrip('/') | 570 self.url = url.rstrip('/') |
| 571 bot_url = self.url | |
| 572 if not bot_url.endswith('/bots'): | |
| 573 bot_url += '/bots' | |
| 574 | |
| 563 with open(client_private_key_file, 'rb') as f: | 575 with open(client_private_key_file, 'rb') as f: |
| 564 client_private_key = f.read() | 576 client_private_key = f.read() |
| 565 self.rpc_server = OAuthRpcServer(url, | 577 logging.info('Using login: %s' % client_email) |
|
Sergey Berezin
2014/04/14 20:59:42
Would it make sense to say 'Using OAuth login: %s'
Sergey Berezin
2014/04/14 22:49:46
What about this comment?
pgervais
2014/04/14 23:10:43
Sorry, I've overlooked it completely. I definitely
| |
| 566 client_id, | 578 self.rpc_server = OAuthRpcServer(bot_url, |
| 579 client_email, | |
| 567 client_private_key, | 580 client_private_key, |
| 568 private_key_password=private_key_password, | 581 private_key_password=private_key_password, |
| 569 extra_headers=extra_headers or {}) | 582 extra_headers=extra_headers or {}) |
| 570 self._xsrf_token = None | 583 self._xsrf_token = None |
| 571 self._xsrf_token_time = None | 584 self._xsrf_token_time = None |
| 572 | 585 |
| 573 | 586 |
| 574 class CachingRietveld(Rietveld): | 587 class CachingRietveld(Rietveld): |
| 575 """Caches the common queries. | 588 """Caches the common queries. |
| 576 | 589 |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 696 def trigger_try_jobs( # pylint:disable=R0201 | 709 def trigger_try_jobs( # pylint:disable=R0201 |
| 697 self, issue, patchset, reason, clobber, revision, builders_and_tests, | 710 self, issue, patchset, reason, clobber, revision, builders_and_tests, |
| 698 master=None): | 711 master=None): |
| 699 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 712 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 700 (builders_and_tests, issue)) | 713 (builders_and_tests, issue)) |
| 701 | 714 |
| 702 def trigger_distributed_try_jobs( # pylint:disable=R0201 | 715 def trigger_distributed_try_jobs( # pylint:disable=R0201 |
| 703 self, issue, patchset, reason, clobber, revision, masters): | 716 self, issue, patchset, reason, clobber, revision, masters): |
| 704 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 717 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
| 705 (masters, issue)) | 718 (masters, issue)) |
| OLD | NEW |