Source code for wolframclient.evaluation.cloud.oauth

# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import logging

from wolframclient.evaluation.cloud.base import (OAuthSessionBase,
                                                 UserIDPassword)
from wolframclient.exception import AuthenticationException
from wolframclient.utils import six
from wolframclient.utils.api import oauth, urllib

logger = logging.getLogger(__name__)

__all__ = ['OAuth1RequestsSyncSession', 'XAuthRequestsSyncSession']


class OAuthRequestsSyncSessionBase(OAuthSessionBase):
    """ A wrapper around the OAuth client taking care of fetching the various oauth tokens,
    preparing data as expected by the requests library.
    """

    def __init__(self,
                 http_session,
                 server,
                 consumer_key,
                 consumer_secret,
                 signature_method=None,
                 client_class=None):
        super().__init__(
            server,
            consumer_key,
            consumer_secret,
            signature_method=signature_method,
            client_class=client_class or oauth.Client)
        self.http_session = http_session
        self.verify = self.server.certificate

    def _check_response(self, response):
        msg = None
        if response.status_code == 200:
            return
        try:
            as_json = response.json()
            msg = as_json.get('message', None)
            raise AuthenticationException(response, msg)
        # msg is None if response is not JSON, but it's fine.
        except Exception:
            msg = 'Request failed with status %i' % response.status_code
            raise AuthenticationException(response, msg=msg)

    def signed_request(self, uri, headers={}, body={}, files={},
                       method='POST'):
        if not self.authorized():
            self.authenticate()

        req_headers = {}
        for k, v in headers.items():
            req_headers[k] = v
        sign_body = False
        #if files is set, it's a multipart request.
        if len(files) == 0:
            if isinstance(body, dict):
                # url encode the body
                encoded_body = urllib.urlencode(body)
                sign_body = True
                if 'Content-Type' not in req_headers:
                    logger.info(
                        'Content type not provided by user. Setting it to "application/x-www-form-urlencoded".'
                    )
                    req_headers[
                        'Content-Type'] = 'application/x-www-form-urlencoded'
            elif isinstance(body, six.string_types) or isinstance(
                    body, six.binary_type):
                # application/octet-stream for binary data?
                if 'Content-Type' not in req_headers:
                    logger.info(
                        'Content type not provided by user. Setting it to "text/plain".'
                    )
                    req_headers['Content-Type'] = 'text/plain'
                    sign_body = False
                elif 'application/x-www-form-urlencoded' == req_headers[
                        'Content-Type']:
                    encoded_body = body
                    sign_body = True
                else:
                    sign_body = False
            else:
                logger.fatal('Invalid body: %s', body)
                raise ValueError('Body must be dict or string type.')

        uri, req_headers, signed_body = self._client.sign(
            uri,
            method,
            body=encoded_body if sign_body else None,
            headers=req_headers,
            realm=self.server.cloudbase)
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug('Signed uri: %s', uri)
            logger.debug('Signed header: %s', req_headers)
            logger.debug('Is body signed: %s', sign_body)

        return self.http_session.request(
            method,
            uri,
            headers=req_headers,
            data=signed_body if sign_body else body,
            files=files,
            verify=self.verify)


[docs]class OAuth1RequestsSyncSession(OAuthRequestsSyncSessionBase): """ Oauth1 authentication using secured authentication key, as expected by the requests library. """
[docs] def authenticate(self): self.set_oauth_request_token() self.set_oauth_access_token() self._update_client()
[docs] def set_oauth_request_token(self): if logger.isEnabledFor(logging.DEBUG): logger.debug('Fetching oauth request token from: %s', self.server.request_token_endpoint) logging.disable(logging.DEBUG) token_client = self.client_class( self.consumer_key, client_secret=self.consumer_secret) uri, headers, body = token_client.sign( self.server.request_token_endpoint, "POST") response = self.http_session.post( uri, headers=headers, data=body, verify=self.verify) logging.disable(logging.NOTSET) self._check_response(response) self._update_token_from_request_body(response.content)
[docs] def set_oauth_access_token(self): if logger.isEnabledFor(logging.DEBUG): logger.debug('Fetching oauth access token from %s', self.server.access_token_endpoint) access_client = self.client_class( self.consumer_key, client_secret=self.consumer_secret, resource_owner_key=self._oauth_token, resource_owner_secret=self._oauth_token_secret) uri, headers, body = access_client.sign( self.server.access_token_endpoint, "POST") access_response = self.http_session.post( uri, headers=headers, data=body, verify=self.verify) self._check_response(access_response) self._update_token_from_request_body(access_response.content)
[docs]class XAuthRequestsSyncSession(OAuthRequestsSyncSessionBase): """ XAuth authentication as expected by the requests library. xauth authenticates with user and password, but requires a specific server configuration. """ def __init__(self, userid_password, http_session, server, consumer_key, consumer_secret, signature_method=None, client_class=oauth.Client): super().__init__( http_session, server, server.xauth_consumer_key, server.xauth_consumer_secret, signature_method=signature_method, client_class=client_class) if not self.server.is_xauth(): raise AuthenticationException( 'XAuth is not configured for this server. Missing xauth consumer key and/or secret.' ) if isinstance(userid_password, tuple) and len(userid_password) == 2: self.xauth_credentials = UserIDPassword(*userid_password) elif isinstance(userid_password, UserIDPassword): self.xauth_credentials = userid_password else: raise ValueError( 'User ID and password must be specified as a tuple or a UserIDPassword instance.' )
[docs] def authenticate(self): if logger.isEnabledFor(logging.DEBUG): logger.debug('xauth authentication of user %s', user) if not self.server.is_xauth(): raise AuthenticationException( 'XAuth is not configured. Missing consumer key and/or secret.') #todo use xauth server key/secret client = self.client_class(self.consumer_key, self.consumer_secret) params = {} params["x_auth_username"] = self.xauth_credentials.user params["x_auth_password"] = self.xauth_credentials.password params["x_auth_mode"] = "client_auth" # avoid dumping password in log files. logging.disable(logging.DEBUG) uri, headers, body = client.sign( self.server.access_token_endpoint, 'POST', headers=self.DEFAULT_CONTENT_TYPE, body=params) response = self.http_session.post( uri, headers=headers, data=body, verify=self.verify) logging.disable(logging.NOTSET) self._check_response(response) self._update_token_from_request_body(response.content) self._update_client()