idan/oauthlib — pull request #239 by al-the-x (2014-03-26 04:07:29+00:00)
Title: Add support for custom signature methods to `oauth1.Client`

In attempting to connect to Pearson's Learning Center LMS product using their custom OAuth 1.0A Provider implementation via `requests` + `requests-oauthlib` + `oauthlib`, I need the ability to define a custom signature method for requests made via `oauth1.Client`. Pearson requires `CMAC-AES` encryption to sign requests and does so in a manner wholly different from the standard `HMAC-SHA1` method defined in the RFC. However, this is their prerogative as the Provider, per the RFC:

> OAuth provides three methods for the client to prove its rightful ownership of the credentials: "HMAC-SHA1", "RSA-SHA1", and "PLAINTEXT". These methods are generally referred to as signature methods, even though "PLAINTEXT" does not involve a signature. In addition, "RSA-SHA1" utilizes an RSA key instead of the shared-secrets associated with the client credentials.

> OAuth does not mandate a particular signature method, as each implementation can have its own unique requirements. Servers are free to implement and document their own custom methods. Recommending any particular method is beyond the scope of this specification. Implementers should review the Security Considerations section (Section 4) before deciding on which method to support.

Currently, `oauth1.Client` only permits signature methods `HMAC-SHA1`, `RSA-SHA1` and `PLAINTEXT` via hard-coded conditional statements; see:

* [`oauthlib/oauth1/rfc5849/__init__.py:30-133`](https://github.com/idan/oauthlib/blob/a66fe9800c7c7732491a213e1cacf7b82f2b1282/oauthlib/oauth1/rfc5849/__init__.py#L30-L33) – constants for valid signature methods
* [`oauthlib/oauth1/rfc5849/__init__.py:107-110`](https://github.com/idan/oauthlib/blob/a66fe9800c7c7732491a213e1cacf7b82f2b1282/oauthlib/oauth1/rfc5849/__init__.py#L107-L110) – process `PLAINTEXT` signature method
* [`oauthlib/oauth1/rfc5849/__init__.py:131-137`](https://github.com/idan/oauthlib/blob/a66fe9800c7c7732491a213e1cacf7b82f2b1282/oauthlib/oauth1/rfc5849/__init__.py#L131-L137) – process `HMAC-SHA1` and `RSA-SHA1` signature methods; raise `ValueError` otherwise

*Proposed*

In order to support custom signature methods, `oauth.Client` should provide an API for registering new signature methods by name. For example, to provide `PLAINTEXT` method:

from oauthlib.oauth1 import Client

## Register the PLAINTEXT signature method as a callback...
Client.register_signature_method("PLAINTEXT",
lambda client, base_string:
return base_string)

Then `oauth1.Client.sign` should look up the signature method in the registered methods and invoke the registered callable, throwing `ValueError` if the value of `oauth_signature_method` has not been registered. Callables passed to `oauth1.Client.register_signature_method` should accept the `client` instance (`self`) and the `base_string` to be signed, as whatever information the signature method needs should be available as a property on the client instance. The current signature method functions – `sign_hmac_sha1`, `sign_rsa_sha1`, and `sign_plaintext` – can easily be refactored to match this signature.

Any existing tests for `oauth1.Client` or the `sign_*` functions should be updated accordingly, and new tests for `oauth1.Client.register_signature_method` should be added, along with supporting tests (re #28).