Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: rietveld.py

Issue 183793010: Added OAuth2 authentication to apply_issue (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: moved diff in place Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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:
11 - Nobody else than issue owner can upload a patch set 11 - Nobody else than issue owner can upload a patch set
12 - Verifies the issue owner credentials when creating new issues 12 - Verifies the issue owner credentials when creating new issues
13 - A issue owner can't change once the issue is created 13 - A issue owner can't change once the issue is created
14 - A patch set cannot be modified 14 - A patch set cannot be modified
15 """ 15 """
16 16
17 import copy 17 import copy
18 import json 18 import json
19 import logging 19 import logging
20 import re 20 import re
21 import ssl 21 import ssl
22 import time 22 import time
23 import urllib
23 import urllib2 24 import urllib2
25 import urlparse
26
27 import patch
24 28
25 from third_party import upload 29 from third_party import upload
26 import patch 30 from third_party.oauth2client.client import SignedJwtAssertionCredentials
31 from third_party import httplib2
27 32
28 # Hack out upload logging.info() 33 # Hack out upload logging.info()
29 upload.logging = logging.getLogger('upload') 34 upload.logging = logging.getLogger('upload')
30 # Mac pylint choke on this line. 35 # Mac pylint choke on this line.
31 upload.logging.setLevel(logging.WARNING) # pylint: disable=E1103 36 upload.logging.setLevel(logging.WARNING) # pylint: disable=E1103
32 37
33 38
34 class Rietveld(object): 39 class Rietveld(object):
35 """Accesses rietveld.""" 40 """Accesses rietveld."""
36 def __init__(self, url, email, password, extra_headers=None): 41 def __init__(self, url, email, password, extra_headers=None):
37 self.url = url.rstrip('/') 42 self.url = url.rstrip('/')
38 # TODO(maruel): It's not awesome but maybe necessary to retrieve the value. 43 # TODO(maruel): It's not awesome but maybe necessary to retrieve the value.
39 # It happens when the presubmit check is ran out of process, the cookie 44 # It happens when the presubmit check is ran out of process, the cookie
40 # needed to be recreated from the credentials. Instead, it should pass the 45 # needed to be recreated from the credentials. Instead, it should pass the
41 # email and the cookie. 46 # email and the cookie.
42 self.email = email
43 self.password = password
44 if email and password: 47 if email and password:
45 get_creds = lambda: (email, password) 48 get_creds = lambda: (email, password)
46 self.rpc_server = upload.HttpRpcServer( 49 self.rpc_server = upload.HttpRpcServer(
47 self.url, 50 self.url,
48 get_creds, 51 get_creds,
49 extra_headers=extra_headers or {}) 52 extra_headers=extra_headers or {})
50 else: 53 else:
51 if email == '': 54 if email == '':
52 # If email is given as an empty string, then assume we want to make 55 # If email is given as an empty string, then assume we want to make
53 # requests that do not need authentication. Bypass authentication by 56 # requests that do not need authentication. Bypass authentication by
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 raise 434 raise
432 # If reaching this line, loop again. Uses a small backoff. 435 # If reaching this line, loop again. Uses a small backoff.
433 time.sleep(1+maxtries*2) 436 time.sleep(1+maxtries*2)
434 finally: 437 finally:
435 upload.ErrorExit = old_error_exit 438 upload.ErrorExit = old_error_exit
436 439
437 # DEPRECATED. 440 # DEPRECATED.
438 Send = get 441 Send = get
439 442
440 443
444 class OAuthRpcServer(object):
445 def __init__(self,
446 host,
447 client_id,
448 client_private_key,
449 private_key_password='notasecret',
450 user_agent=None,
451 timeout=None,
452 extra_headers=None):
453 """Wrapper around httplib2.Http() that handles authentication.
454
455 client_id: client id for service account
456 client_private_key: encrypted private key, as a string
457 private_key_password: password used to decrypt the private key
458 """
459
460 # Enforce https
461 host_parts = urlparse.urlparse(host)
462
463 if host_parts.scheme == 'https': # fine
464 self.host = host
465
466 elif host_parts.scheme == 'http':
467 upload.logging.warning('Changing protocol to https')
468 self.host = 'https' + host[4:]
469
M-A Ruel 2014/03/15 01:15:31 optional nit: I would not put these empty lines, i
pgervais 2014/03/17 17:35:11 Done.
470 else:
471 msg = 'Invalid url provided: %s' % host
472 upload.logging.error(msg)
473 raise ValueError(msg)
474
475 self.host = self.host.rstrip('/')
476
477 self.extra_headers = extra_headers or {}
478 creds = SignedJwtAssertionCredentials(
479 client_id,
480 client_private_key,
481 'https://www.googleapis.com/auth/userinfo.email',
482 private_key_password,
483 user_agent=user_agent)
484
485 http = httplib2.Http(timeout=timeout)
486 self._http = creds.authorize(http)
M-A Ruel 2014/03/15 01:15:31 self._http = creds.authorize(httplib2.Http(timeout
pgervais 2014/03/17 17:35:11 Done.
487
488 def Send(self,
489 request_path,
490 payload=None,
491 content_type='application/octet-stream',
492 timeout=None,
493 extra_headers=None,
494 **kwargs):
495 """Send a POST or GET request to the server.
496
497 Args:
498 request_path: path on the server to hit. This is concatenated with the
499 value of 'host' provided to the constructor.
500 payload: request is a POST if not None, GET otherwise
501 timeout: in seconds
502 extra_headers: (dict)
503 """
504 # This method signature should match upload.py:AbstractRpcServer.Send()
505 method = 'GET'
506
507 headers = self.extra_headers.copy()
508 headers.update(extra_headers or {})
509
510 if payload is not None:
511 method = 'POST'
512 headers['Content-Type'] = content_type
513 raise NotImplementedError('POST requests are not yet supported.')
514
515 prev_timeout = self._http.timeout
516 try:
517 if timeout:
518 self._http.timeout = timeout
519 # TODO(pgervais) implement some kind of retry mechanism (see upload.py).
520 url = self.host + request_path
521 if kwargs:
522 url += "?" + urllib.urlencode(kwargs)
523
524 ret = self._http.request(url,
525 method=method,
526 body=payload,
527 headers=headers)
528 return ret[1]
529
530 finally:
531 self._http.timeout = prev_timeout
532
533
534 class JwtOAuth2Rietveld(Rietveld):
535 """Access to Rietveld using OAuth authentication.
536
537 This class is supposed to be used only by bots, since this kind of
538 access is restricted to service accounts.
539 """
540 # The parent__init__ is not called on purpose.
541 # pylint: disable=W0231
542 def __init__(self,
543 url,
544 client_id,
545 client_private_key_file,
546 private_key_password=None,
547 extra_headers=None):
548 if private_key_password is None: # '' means 'empty password'
549 private_key_password = 'notasecret'
550
551 self.url = url.rstrip('/')
552 with open(client_private_key_file, 'rb') as f:
553 client_private_key = f.read()
554 self.rpc_server = OAuthRpcServer(url,
555 client_id,
556 client_private_key,
557 private_key_password=private_key_password,
558 extra_headers=extra_headers or {})
559 self._xsrf_token = None
560 self._xsrf_token_time = None
561
562
441 class CachingRietveld(Rietveld): 563 class CachingRietveld(Rietveld):
442 """Caches the common queries. 564 """Caches the common queries.
443 565
444 Not to be used in long-standing processes, like the commit queue. 566 Not to be used in long-standing processes, like the commit queue.
445 """ 567 """
446 def __init__(self, *args, **kwargs): 568 def __init__(self, *args, **kwargs):
447 super(CachingRietveld, self).__init__(*args, **kwargs) 569 super(CachingRietveld, self).__init__(*args, **kwargs)
448 self._cache = {} 570 self._cache = {}
449 571
450 def _lookup(self, function_name, args, update): 572 def _lookup(self, function_name, args, update):
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 def trigger_try_jobs( # pylint:disable=R0201 681 def trigger_try_jobs( # pylint:disable=R0201
560 self, issue, patchset, reason, clobber, revision, builders_and_tests, 682 self, issue, patchset, reason, clobber, revision, builders_and_tests,
561 master=None): 683 master=None):
562 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % 684 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' %
563 (builders_and_tests, issue)) 685 (builders_and_tests, issue))
564 686
565 def trigger_distributed_try_jobs( # pylint:disable=R0201 687 def trigger_distributed_try_jobs( # pylint:disable=R0201
566 self, issue, patchset, reason, clobber, revision, masters): 688 self, issue, patchset, reason, clobber, revision, masters):
567 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % 689 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' %
568 (masters, issue)) 690 (masters, issue))
OLDNEW
« apply_issue.py ('K') | « apply_issue.py ('k') | third_party/httplib2/LICENSE » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698