joestump / python-oauth2

A fully tested, abstract interface to creating OAuth clients and servers.
MIT License
2.99k stars 911 forks source link

Need a way to add a parameter when using POST with no form #82

Open portante opened 13 years ago

portante commented 13 years ago

It would be helpful to have a way to add a parameter to the authorization header when POSTing with no form body.

For example, imagine a world where somebody would like to be able to have his (or her) POST body contain an XML payload, but still add a parameter to the authorization header:

POST /servic/tokens/temp HTTP/1.1
Host: 192.168.1.2
Accept-Encoding: identity
Content-Length: 11
content-type: text/xml
authorization: OAuth realm="https://192.168.1.2", oauth_body_hash="ytIINryy7%2FaoD1bdgNnDBD0GAEHifo%3D", oauth_nonce="929257241", oauth_timestamp="131208533215", oauth_consumer_key="112723bd7a6e76102d92b0123134563df6", oauth_signature_method="PLAINTEXT", oauth_version="1.0", oauth_signature="1127bda2-6e76-102d-92b0-12123126%26", oauth_callback="url"
user-agent: Python-httplib2/$Rev$

<xml>...</xml>

So if she (or he) used a client request as follows, she (or he, well if he is a she, this is not a problem) would have no way to add the "oauth_callback" to the authorization header, since you can only add those via a form post body:

client = oauth.Client(...)
resp, content = client.request(request_token_url, method="POST", headers={'Content-Type':'text/xml'}, body='<xml>...</xml>')

Perhaps we could add a parameters parameter to the request method of Client, so that non-form POSTs can still add those parameters to the authorization header:

client = oauth.Client(...)
resp, content = client.request(request_token_url, method="POST", headers={'Content-Type':'text/xml'}, body='<xml>...</xml>', parameters=dict(oauth_callback='url'))
portante commented 13 years ago

Here is a suggested code change that addresses this issue, and tries to maintain the existing behavior:

    def request(self, uri, method="GET", body='', headers=None, 
        redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None, parameters=None):
        DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded'

        if not isinstance(headers, dict):
            headers = {}
        if not isinstance(parameters, dict):
            parameters = {}

        if method == "POST":
            headers['Content-Type'] = headers.get('Content-Type', 
                DEFAULT_POST_CONTENT_TYPE)

        is_form_encoded = \
            headers.get('Content-Type') == 'application/x-www-form-urlencoded'

        if is_form_encoded and body:
            parameters.update(parse_qs(body))

        req = Request.from_consumer_and_token(self.consumer, 
            token=self.token, http_method=method, http_url=uri, 
            parameters=parameters, body=body, is_form_encoded=is_form_encoded)

        req.sign_request(self.method, self.consumer, self.token)

        schema, rest = urllib.splittype(uri)
        if rest.startswith('//'):
            hierpart = '//'
        else:
            hierpart = ''
        host, rest = urllib.splithost(rest)

        realm = schema + ':' + hierpart + host

        if is_form_encoded:
            body = req.to_postdata()
        elif method == "GET":
            uri = req.to_url()
        else:
            headers.update(req.to_header(realm=realm))

        return httplib2.Http.request(self, uri, method=method, body=body,
            headers=headers, redirections=redirections,
            connection_type=connection_type)
rafaelabreu commented 8 years ago

+1