def validate(self, uri, params, signature): """Compute the signature for a given request :param uri: full URI that Twilio requested on your server :param params: post vars that Twilio sent with the request :param utf: whether return should be bytestring or unicode (python3) :returns: Boolean indicating if the signature was valid or not """ if params is None: params = {} parsed_uri = urlparse(uri) uri_with_port = add_port(parsed_uri) uri_without_port = remove_port(parsed_uri) signature_bytes = base64.b64decode(signature) # compute signature with Ed25519 algorithm # check signature of uri with and without port, # since sig generation on back end is inconsistent for uri in [uri_without_port, uri_with_port]: try: self._verify_key.verify(self._compute_signed_bytes( uri, params), signature=signature_bytes) except BadSignatureError: continue else: return True return False
def validate(self, uri, params, signature): """Validate a request from Twilio :param uri: full URI that Twilio requested on your server :param params: dictionary of POST variables or string of POST body for JSON requests :param signature: expected signature in HTTP X-Twilio-Signature header :returns: True if the request passes validation, False if not """ if params is None: params = {} parsed_uri = urlparse(uri) if parsed_uri.scheme == "https" and parsed_uri.port: uri = remove_port(parsed_uri) valid_signature = False # Default fail valid_body_hash = True # May not receive body hash, so default succeed query = parse_qs(parsed_uri.query) if "bodySHA256" in query and isinstance(params, string_types): valid_body_hash = compare(self.compute_hash(params), query["bodySHA256"][0]) valid_signature = compare(self.compute_signature(uri, {}), signature) else: valid_signature = compare(self.compute_signature(uri, params), signature) return valid_signature and valid_body_hash
def validate(self, uri, params, signature): """Validate a request from Twilio :param uri: full URI that Twilio requested on your server :param params: dictionary of POST variables or string of POST body for JSON requests :param signature: expected signature in HTTP X-Twilio-Signature header :returns: True if the request passes validation, False if not """ if params is None: params = {} parsed_uri = urlparse(uri) uri_with_port = add_port(parsed_uri) uri_without_port = remove_port(parsed_uri) valid_signature = False # Default fail valid_signature_with_port = False valid_body_hash = True # May not receive body hash, so default succeed query = parse_qs(parsed_uri.query) if "bodySHA256" in query and isinstance(params, string_types): valid_body_hash = compare(self.compute_hash(params), query["bodySHA256"][0]) params = {} # check signature of uri with and without port, # since sig generation on back end is inconsistent valid_signature = compare( self.compute_signature(uri_without_port, params), signature) valid_signature_with_port = compare( self.compute_signature(uri_with_port, params), signature) return valid_body_hash and (valid_signature or valid_signature_with_port)
def iter(self, **kwargs): """ Return all instance resources using an iterator This will fetch a page of resources from the API and yield them in turn. When the page is exhausted, this will make a request to the API to retrieve the next page. Hence you may notice a pattern - the library will loop through 50 objects very quickly, but there will be a delay retrieving the 51st as the library must make another request to the API for resources. Example usage: .. code-block:: python for message in client.messages: print message.sid """ params = transform_params(kwargs) while True: resp, page = self.request("GET", self.uri, params=params) if self.key not in page: raise StopIteration() for ir in page[self.key]: yield self.load_instance(ir) if not page.get('next_page_uri', ''): raise StopIteration() o = urlparse(page['next_page_uri']) params.update(parse_qs(o.query))
def get_hostname(self, uri): """ Determines the proper hostname given edge and region preferences via client configuration or uri. :param str uri: Fully qualified url :returns: The final uri used to make the request :rtype: str """ if not self.edge and not self.region: return uri parsed_url = urlparse(uri) pieces = parsed_url.netloc.split('.') prefix = pieces[0] suffix = '.'.join(pieces[-2:]) region = None edge = None if len(pieces) == 4: # product.region.twilio.com region = pieces[1] elif len(pieces) == 5: # product.edge.region.twilio.com edge = pieces[1] region = pieces[2] edge = self.edge or edge region = self.region or region or (edge and 'us1') parsed_url = parsed_url._replace( netloc='.'.join([part for part in [prefix, edge, region, suffix] if part]) ) return urlunparse(parsed_url)
def request(self, method, url, params=None, data=None, headers=None, auth=None, timeout=None, allow_redirects=False): """Sends an HTTP request :param str method: The HTTP method to use :param str url: The URL to request :param dict params: Query parameters to append to the URL :param dict data: Parameters to go in the body of the HTTP request :param dict headers: HTTP Headers to send with the request :param tuple auth: Basic Auth arguments :param float timeout: Socket/Read timeout for the request :param boolean allow_redirects: Whether or not to allow redirects :return: An http response :rtype: A :class:`Response <twilio.rest.http.response.Response>` object See the requests documentation for explanation of all these parameters """ http = httplib2.Http( timeout=timeout, ca_certs=get_cert_file(), proxy_info=self.proxy_info, ) http.follow_redirects = allow_redirects if auth is not None: http.add_credentials(auth[0], auth[1]) if data is not None: udata = {} for k, v in iteritems(data): key = k.encode('utf-8') if isinstance(v, (list, tuple, set)): udata[key] = [self.encode_atom(x) for x in v] elif isinstance(v, (integer_types, binary_type, string_types)): udata[key] = self.encode_atom(v) else: raise ValueError('data should be an integer, ' 'binary, or string, or sequence ') data = urlencode(udata, doseq=True) if params is not None: enc_params = urlencode(params, doseq=True) if urlparse(url).query: url = '%s&%s' % (url, enc_params) else: url = '%s?%s' % (url, enc_params) resp, content = http.request(url, method, headers=headers, body=data) return Response(int(resp.status), content.decode('utf-8'))
def validate(self, uri, params, signature): """Validate a request from Twilio :param uri: full URI that Twilio requested on your server :param params: post vars that Twilio sent with the request :param signature: expected signature in HTTP X-Twilio-Signature header :returns: True if the request passes validation, False if not """ parsed_uri = urlparse(uri) if parsed_uri.scheme == "https" and parsed_uri.port: uri = remove_port(parsed_uri) return compare(self.compute_signature(uri, params), signature)
def validate(self, uri, params, signature): """Validate a request from Twilio :param uri: full URI that Twilio requested on your server :param params: post vars that Twilio sent with the request :param signature: expected signature in HTTP X-Twilio-Signature header :returns: True if the request passes validation, False if not """ parsed_uri = urlparse(uri) # https://www.twilio.com/docs/api/security#notes says that HTTPS port should be ommited # but if https port isn't default - validation webhook fails # related issue https://github.com/twilio/twilio-python/issues/402 if parsed_uri.scheme == "https" and parsed_uri.port == 443: uri = remove_port(parsed_uri) return compare(self.compute_signature(uri, params), signature)
def make_request(method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=False, proxies=None): """Sends an HTTP request Returns :class:`Response <models.Response>` See the requests documentation for explanation of all these parameters Currently proxies, files, and cookies are all ignored """ http = httplib2.Http(timeout=timeout, ca_certs=get_cert_file()) http.follow_redirects = allow_redirects if auth is not None: http.add_credentials(auth[0], auth[1]) if data is not None: udata = {} for k, v in iteritems(data): key = k.encode('utf-8') if isinstance(v, (integer_types, binary_type)): udata[key] = v elif isinstance(v, string_types): udata[key] = v.encode('utf-8') else: raise ValueError('data should be an integer, ' 'binary, or string') data = urlencode(udata) if params is not None: enc_params = urlencode(params, doseq=True) if urlparse(url).query: url = '%s&%s' % (url, enc_params) else: url = '%s?%s' % (url, enc_params) resp, content = http.request(url, method, headers=headers, body=data) # Format httplib2 request as requests object return Response(resp, content.decode('utf-8'), url)
def _build_validation_payload(self, request): """ Extract relevant information from request to build a ClientValidationJWT :param PreparedRequest request: request we will extract information from. :return: ValidationPayload """ parsed = urlparse(request.url) path = parsed.path query_string = parsed.query or '' return ValidationPayload( method=request.method, path=path, query_string=query_string, all_headers=request.headers, signed_headers=ValidationClient.__SIGNED_HEADERS, body=request.body or '')
def _build_validation_payload(self, request): """ Extract relevant information from request to build a ClientValidationJWT :param PreparedRequest request: request we will extract information from. :return: ValidationPayload """ parsed = urlparse(request.url) path = parsed.path query_string = parsed.query or '' return ValidationPayload( method=request.method, path=path, query_string=query_string, all_headers=request.headers, signed_headers=ValidationClient.__SIGNED_HEADERS, body=request.body or '' )
def iter(self, **kwargs): """ Return all instance resources using an iterator """ params = transform_params(kwargs) while True: resp, page = self.request("GET", self.uri, params=params) if self.key not in page: raise StopIteration() for ir in page[self.key]: yield self.load_instance(ir) if not page.get('next_page_uri', ''): raise StopIteration() o = urlparse(page['next_page_uri']) params.update(parse_qs(o.query))
def make_request(method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=False, proxies=None): """Sends an HTTP request Returns :class:`Response <models.Response>` See the requests documentation for explanation of all these parameters Currently proxies, files, and cookies are all ignored """ http = httplib2.Http(timeout=timeout) http.follow_redirects = allow_redirects if auth is not None: http.add_credentials(auth[0], auth[1]) if data is not None: udata = {} for k, v in iteritems(data): key = k.encode('utf-8') if isinstance(v, (integer_types, binary_type)): udata[key] = v elif isinstance(v, string_types): udata[key] = v.encode('utf-8') else: raise ValueError('data should be an integer, ' 'binary, or string') data = urlencode(udata) if params is not None: enc_params = urlencode(params, doseq=True) if urlparse(url).query: url = '%s&%s' % (url, enc_params) else: url = '%s?%s' % (url, enc_params) resp, content = http.request(url, method, headers=headers, body=data) # Format httplib2 request as requests object return Response(resp, content.decode('utf-8'), url)
def make_request(method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=False, proxies=None): """Sends an HTTP request :param str method: The HTTP method to use :param str url: The URL to request :param dict params: Query parameters to append to the URL :param dict data: Parameters to go in the body of the HTTP request :param dict headers: HTTP Headers to send with the request :param float timeout: Socket/Read timeout for the request :return: An http response :rtype: A :class:`Response <models.Response>` object See the requests documentation for explanation of all these parameters Currently proxies, files, and cookies are all ignored """ http = httplib2.Http( timeout=timeout, ca_certs=get_cert_file(), proxy_info=Connection.proxy_info(), ) http.follow_redirects = allow_redirects if auth is not None: http.add_credentials(auth[0], auth[1]) def encode_atom(atom): if isinstance(atom, (integer_types, binary_type)): return atom elif isinstance(atom, string_types): return atom.encode('utf-8') else: raise ValueError('list elements should be an integer, ' 'binary, or string') if data is not None: udata = {} for k, v in iteritems(data): key = k.encode('utf-8') if isinstance(v, (list, tuple, set)): udata[key] = [encode_atom(x) for x in v] elif isinstance(v, (integer_types, binary_type, string_types)): udata[key] = encode_atom(v) else: raise ValueError('data should be an integer, ' 'binary, or string, or sequence ') data = urlencode(udata, doseq=True) if params is not None: enc_params = urlencode(params, doseq=True) if urlparse(url).query: url = '%s&%s' % (url, enc_params) else: url = '%s?%s' % (url, enc_params) resp, content = http.request(url, method, headers=headers, body=data) # Format httplib2 request as requests object return Response(resp, content.decode('utf-8'), url)
def _get_host(self, request): """Pull the Host out of the request""" parsed = urlparse(request.url) return parsed.netloc