Http requests for Compass consoles.

   * `requests` library is expensive to import, so make user-friendly wrapper of urllib3.


### Usage

All modules that depend on `requests` should be modified to rely on `lcrequests`.



### Oauth2 client support

Oauth2 support is extracted from https://github.com/antechrestos/OAuth2Client and modified
to remove the requests dependency.

It can handle the following authentication flows:

* Authorization code
* User Credentials
* Client Credentials

### Authorization code

Since authorization code process needs the user to accept the access to its data by the application, the library
starts locally a http server. You may put the host part of the ``redirect_uri`` parameter in your *hosts* file
pointing to your loop-back address. The server waits a ``GET`` requests with the  ``code`` as a query parameter.

Getting a couple of access token may be done like this:

```python
from lcrequests.oauth2 import CredentialManager, ServiceInformation
import logging
_logger = logging.getLogger(__name__)

scopes = ['scope_1', 'scope_2']

service_information = ServiceInformation(
    'https://authorization-server/oauth/authorize',
    'https://token-server/oauth/token',
    'client_id',
    'client_secret',
    scopes,
)
manager = CredentialManager(service_information)
redirect_uri = 'http://somewhere.io:8080/oauth/code'

# Builds the authorization url and starts the local server according to the redirect_uri parameter
url = manager.init_authorize_code_process(redirect_uri, 'state_test')
_logger.info('Open this url in your browser\n%s', url)

code = manager.wait_and_terminate_authorize_code_process()
# From this point the http server is opened on 8080 port and wait to receive a single GET request
# All you need to do is open the url and the process will go on
# (as long you put the host part of your redirect uri in your host file)
# when the server gets the request with the code (or error) in its query parameters
_logger.debug('Code got = %s', code)
manager.init_with_authorize_code(redirect_uri, code)
_logger.debug('Access got = %s', manager._access_token)
# Here access and refresh token may be used with self.refresh_token
```

### User credentials

Getting a couple of access and refresh token is much easier:

```python
from lcrequests.oauth2 import CredentialManager, ServiceInformation
import logging
_logger = logging.getLogger(__name__)

scopes = ['scope_1', 'scope_2']

service_information = ServiceInformation(
    'https://authorization-server/oauth/authorize',
    'https://token-server/oauth/token',
    'client_id',
    'client_secret',
    scopes
)
manager = CredentialManager(service_information)
manager.init_with_user_credentials('login', 'password')
_logger.debug('Access got = %s', manager._access_token)
# Here access and refresh token may be used
```

### Client credentials

You can also get a token with client credentials process

```python
from lcrequests.oauth2 import CredentialManager, ServiceInformation

manager = CredentialManager(service_information)
manager.init_with_client_credentials()
# here application admin operation may be called
```

### Refresh token

Provided that you kept a previous ``refresh_token``, you can initiate your credential manager with it:

```python
from lcrequests.oauth2 import CredentialManager, ServiceInformation

manager = CredentialManager(service_information)
manager.init_with_token('my saved refreshed token')
```

### Token expiration


``CredentialManager`` class handle token expiration by calling the ``CredentialManager._is_token_expired`` static method.
This implementation is not accurate for all OAuth server implementation. You'd better extend  ``CredentialManager`` class
and override ``_is_token_expired`` method.

### Read other fields from token response

``CredentialManager`` can be subclassed to handle other token response fields such as ``id_token`` in OpenId protocol.

```python
from lcrequests.oauth2 import CredentialManager

class OpenIdCredentialManager(CredentialManager):
    def __init__(self, service_information, proxies=None):
        super().__init__(service_information, proxies)
        self.id_token = None

    def _process_token_response(self,  token_response, refresh_token_mandatory):
        id_token = token_response.get('id_token')
        OpenIdCredentialManager._check_id(id_token)
        super()._process_token_response(token_response, refresh_token_mandatory)
        self.id_token = id_token

    @staticmethod
    def _check_id(id_token):
        # check that open id token is valid
        pass
```
