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 |