def prepare_request_uri_query(oauth_params, uri): """Prepare the Request URI Query. Per `section 3.5.3`_ of the spec. .. _`section 3.5.3`: http://tools.ietf.org/html/rfc5849#section-3.5.3 """ # append OAuth params to the existing set of query components sch, net, path, par, query, fra = urlparse(uri) query = urlencode( _append_params(oauth_params, extract_params(query) or [])) return urlunparse((sch, net, path, par, query, fra))
def __call__(self, r): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set a guess is made. """ # Overwriting url is safe here as request will not modify it past # this point. log.debug('Signing request %s using client %s', r, self.client) content_type = r.headers.get('Content-Type', '') if (not content_type and extract_params(r.body) or self.client.signature_type == SIGNATURE_TYPE_BODY): content_type = CONTENT_TYPE_FORM_URLENCODED if not isinstance(content_type, unicode): content_type = content_type.decode('utf-8') is_form_encoded = (CONTENT_TYPE_FORM_URLENCODED in content_type) log.debug('Including body in call to sign: %s', is_form_encoded or self.force_include_body) if is_form_encoded: r.headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED r.url, headers, r.body = self.client.sign( unicode(r.url), unicode(r.method), r.body or '', r.headers) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. r.url, headers, r.body = self.client.sign( unicode(r.url), unicode(r.method), r.body or '', r.headers) else: # Omit body data in the signing of non form-encoded requests r.url, headers, _ = self.client.sign( unicode(r.url), unicode(r.method), None, r.headers) r.prepare_headers(headers) r.url = to_native_string(r.url) log.debug('Updated url: %s', r.url) log.debug('Updated headers: %s', headers) log.debug('Updated body: %r', r.body) return r
def collect_parameters(uri_query='', body=[], headers=None, exclude_oauth_signature=True, with_realm=False): """**Parameter Sources** Parameters starting with `oauth_` will be unescaped. Body parameters must be supplied as a dict, a list of 2-tuples, or a formencoded query string. Headers must be supplied as a dict. Per `section 3.4.1.3.1`_ of the spec. For example, the HTTP request:: POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Authorization: OAuth realm="Example", oauth_consumer_key="9djdj82h48djs9d2", oauth_token="kkk9d7dh3k39sjv7", oauth_signature_method="HMAC-SHA1", oauth_timestamp="137131201", oauth_nonce="7d8f3e4a", oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D" c2&a3=2+q contains the following (fully decoded) parameters used in the signature base sting:: +------------------------+------------------+ | Name | Value | +------------------------+------------------+ | b5 | =%3D | | a3 | a | | c@ | | | a2 | r b | | oauth_consumer_key | 9djdj82h48djs9d2 | | oauth_token | kkk9d7dh3k39sjv7 | | oauth_signature_method | HMAC-SHA1 | | oauth_timestamp | 137131201 | | oauth_nonce | 7d8f3e4a | | c2 | | | a3 | 2 q | +------------------------+------------------+ Note that the value of "b5" is "=%3D" and not "==". Both "c@" and "c2" have empty values. While the encoding rules specified in this specification for the purpose of constructing the signature base string exclude the use of a "+" character (ASCII code 43) to represent an encoded space character (ASCII code 32), this practice is widely used in "application/x-www-form-urlencoded" encoded values, and MUST be properly decoded, as demonstrated by one of the "a3" parameter instances (the "a3" parameter is used twice in this request). .. _`section 3.4.1.3.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 """ headers = headers or {} params = [] # The parameters from the following sources are collected into a single # list of name/value pairs: # * The query component of the HTTP request URI as defined by # `RFC3986, Section 3.4`_. The query component is parsed into a list # of name/value pairs by treating it as an # "application/x-www-form-urlencoded" string, separating the names # and values and decoding them as defined by # `W3C.REC-html40-19980424`_, Section 17.13.4. # # .. _`RFC3986, Section 3.4`: http://tools.ietf.org/html/rfc3986#section-3.4 # .. _`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 if uri_query: params.extend(urldecode(uri_query)) # * The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if # present. The header's content is parsed into a list of name/value # pairs excluding the "realm" parameter if present. The parameter # values are decoded as defined by `Section 3.5.1`_. # # .. _`Section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1 if headers: headers_lower = dict((k.lower(), v) for k, v in headers.items()) authorization_header = headers_lower.get('authorization') if authorization_header is not None: params.extend([i for i in utils.parse_authorization_header( authorization_header) if with_realm or i[0] != 'realm']) # * The HTTP request entity-body, but only if all of the following # conditions are met: # * The entity-body is single-part. # # * The entity-body follows the encoding requirements of the # "application/x-www-form-urlencoded" content-type as defined by # `W3C.REC-html40-19980424`_. # * The HTTP request entity-header includes the "Content-Type" # header field set to "application/x-www-form-urlencoded". # # .._`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424 # TODO: enforce header param inclusion conditions bodyparams = extract_params(body) or [] params.extend(bodyparams) # ensure all oauth params are unescaped unescaped_params = [] for k, v in params: if k.startswith('oauth_'): v = utils.unescape(v) unescaped_params.append((k, v)) # The "oauth_signature" parameter MUST be excluded from the signature # base string if present. if exclude_oauth_signature: unescaped_params = list(filter(lambda i: i[0] != 'oauth_signature', unescaped_params)) return unescaped_params