phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | #-*- coding: utf-8 -*- |
| 3 | # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| 4 | # |
| 5 | # Use of this source code is governed by a BSD-style license |
| 6 | # that can be found in the LICENSE file in the root of the source |
| 7 | # tree. An additional intellectual property rights grant can be found |
| 8 | # in the file PATENTS. All contributing project authors may |
| 9 | # be found in the AUTHORS file in the root of the source tree. |
| 10 | |
| 11 | """Contains utilities for communicating with the dashboard.""" |
| 12 | |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 13 | import httplib |
| 14 | import shelve |
| 15 | import oauth.oauth as oauth |
| 16 | |
| 17 | import constants |
| 18 | |
| 19 | |
| 20 | class FailedToReadRequiredInputFile(Exception): |
| 21 | pass |
| 22 | |
| 23 | |
| 24 | class FailedToReportToDashboard(Exception): |
| 25 | pass |
| 26 | |
| 27 | |
| 28 | class DashboardConnection: |
| 29 | """Helper class for pushing data to the dashboard. |
| 30 | |
| 31 | This class deals with most of details for accessing protected resources |
| 32 | (i.e. data-writing operations) on the dashboard. Such operations are |
| 33 | authenticated using OAuth. This class requires a consumer secret and a |
| 34 | access token. |
| 35 | |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 36 | The access token and consumer secrets are stored as files on disk in the |
| 37 | working directory of the scripts. Both files are created by the |
| 38 | request_oauth_permission script. |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 39 | """ |
| 40 | |
| 41 | def __init__(self, consumer_key): |
| 42 | self.consumer_key_ = consumer_key |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 43 | self.consumer_secret_ = None |
| 44 | self.access_token_string_ = None |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 45 | |
| 46 | def read_required_files(self, consumer_secret_file, access_token_file): |
| 47 | """Reads required data for making OAuth requests. |
| 48 | |
| 49 | Args: |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 50 | consumer_secret_file: A shelve file with an entry consumer_secret |
| 51 | containing the consumer secret in string form. |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 52 | access_token_file: A shelve file with an entry access_token |
| 53 | containing the access token in string form. |
| 54 | """ |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 55 | self.access_token_string_ = self._read_access_token(access_token_file) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 56 | self.consumer_secret_ = self._read_consumer_secret(consumer_secret_file) |
| 57 | |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 58 | def send_post_request(self, url, parameters): |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 59 | """Sends an OAuth request for a protected resource in the dashboard. |
| 60 | |
| 61 | Use this when you want to report new data to the dashboard. You must have |
| 62 | called the read_required_files method prior to calling this method, since |
| 63 | that method will read in the consumer secret and access token we need to |
| 64 | make the OAuth request. These concepts are described in the class |
| 65 | description. |
| 66 | |
phoglund@webrtc.org | 86ce46d | 2012-02-06 10:55:12 +0000 | [diff] [blame] | 67 | The server is expected to respond with HTTP status 200 and a completely |
| 68 | empty response if the call failed. The server may put diagnostic |
| 69 | information in the response. |
| 70 | |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 71 | Args: |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 72 | url: An absolute url within the dashboard domain, for example |
| 73 | http://webrtc-dashboard.appspot.com/add_coverage_data. |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 74 | parameters: A dict which maps from POST parameter names to values. |
| 75 | |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 76 | Raises: |
| 77 | FailedToReportToDashboard: If the dashboard didn't respond |
phoglund@webrtc.org | 86ce46d | 2012-02-06 10:55:12 +0000 | [diff] [blame] | 78 | with HTTP 200 to our request or if the response is non-empty. |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 79 | """ |
| 80 | consumer = oauth.OAuthConsumer(self.consumer_key_, self.consumer_secret_) |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 81 | access_token = oauth.OAuthToken.from_string(self.access_token_string_) |
| 82 | |
| 83 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( |
| 84 | consumer, |
| 85 | token=access_token, |
| 86 | http_method='POST', |
| 87 | http_url=url, |
| 88 | parameters=parameters) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 89 | |
| 90 | signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1() |
| 91 | oauth_request.sign_request(signature_method_hmac_sha1, consumer, |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 92 | access_token) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 93 | |
| 94 | connection = httplib.HTTPConnection(constants.DASHBOARD_SERVER) |
| 95 | |
| 96 | headers = {'Content-Type': 'application/x-www-form-urlencoded'} |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 97 | connection.request('POST', url, body=oauth_request.to_postdata(), |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 98 | headers=headers) |
| 99 | |
| 100 | response = connection.getresponse() |
| 101 | connection.close() |
| 102 | |
| 103 | if response.status != 200: |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 104 | message = ('Failed to report to %s: got response %d (%s)' % |
| 105 | (url, response.status, response.reason)) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 106 | raise FailedToReportToDashboard(message) |
| 107 | |
phoglund@webrtc.org | 86ce46d | 2012-02-06 10:55:12 +0000 | [diff] [blame] | 108 | # The response content should be empty on success, so check that: |
| 109 | response_content = response.read() |
| 110 | if response_content: |
| 111 | message = ('Dashboard reported the following error: %s.' % |
| 112 | response_content) |
| 113 | raise FailedToReportToDashboard(message) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 114 | |
| 115 | def _read_access_token(self, filename): |
phoglund@webrtc.org | 914ef27 | 2012-02-27 15:42:25 +0000 | [diff] [blame] | 116 | return self._read_shelve(filename, 'access_token') |
| 117 | |
| 118 | def _read_consumer_secret(self, filename): |
| 119 | return self._read_shelve(filename, 'consumer_secret') |
| 120 | |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 121 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 122 | def _read_shelve(filename, key): |
| 123 | input_file = shelve.open(filename) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 124 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 125 | if not input_file.has_key(key): |
| 126 | raise FailedToReadRequiredInputFile('Missing correct %s file in current ' |
| 127 | 'directory. You may have to run ' |
| 128 | 'request_oauth_permission.py.' % |
| 129 | filename) |
phoglund@webrtc.org | d4f0a0e | 2012-02-01 10:59:23 +0000 | [diff] [blame] | 130 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 131 | result = input_file[key] |
| 132 | input_file.close() |
| 133 | |
| 134 | return result |