Source code for mohawk.sender

import logging

from .base import (default_ts_skew_in_seconds,
                   HawkAuthority,
                   Resource,
                   EmptyValue)
from .util import (calculate_mac,
                   parse_authorization_header,
                   validate_credentials)

__all__ = ['Sender']
log = logging.getLogger(__name__)


[docs]class Sender(HawkAuthority): """ A Hawk authority that will emit requests and verify responses. :param credentials: Dict of credentials with keys ``id``, ``key``, and ``algorithm``. See :ref:`usage` for an example. :type credentials: dict :param url: Absolute URL of the request. :type url: str :param method: Method of the request. E.G. POST, GET :type method: str :param content=EmptyValue: Byte string of request body or a file-like object. :type content=EmptyValue: str or file-like object :param content_type=EmptyValue: content-type header value for request. :type content_type=EmptyValue: str :param always_hash_content=True: When True, ``content`` and ``content_type`` must be provided. Read :ref:`skipping-content-checks` to learn more. :type always_hash_content=True: bool :param nonce=None: A string that when coupled with the timestamp will uniquely identify this request to prevent replays. If None, a nonce will be generated for you. :type nonce=None: str :param ext=None: An external `Hawk`_ string. If not None, this value will be signed so that the receiver can trust it. :type ext=None: str :param app=None: A `Hawk`_ application string. If not None, this value will be signed so that the receiver can trust it. :type app=None: str :param dlg=None: A `Hawk`_ delegation string. If not None, this value will be signed so that the receiver can trust it. :type dlg=None: str :param seen_nonce=None: A callable that returns True if a nonce has been seen. See :ref:`nonce` for details. :type seen_nonce=None: callable .. _`Hawk`: https://github.com/hueniverse/hawk """ #: Value suitable for an ``Authorization`` header. request_header = None def __init__(self, credentials, url, method, content=EmptyValue, content_type=EmptyValue, always_hash_content=True, nonce=None, ext=None, app=None, dlg=None, seen_nonce=None, # For easier testing: _timestamp=None): self.reconfigure(credentials) self.request_header = None self.seen_nonce = seen_nonce log.debug('generating request header') self.req_resource = Resource(url=url, credentials=self.credentials, ext=ext, app=app, dlg=dlg, nonce=nonce, method=method, content=content, always_hash_content=always_hash_content, timestamp=_timestamp, content_type=content_type) mac = calculate_mac('header', self.req_resource, self.req_resource.gen_content_hash()) self.request_header = self._make_header(self.req_resource, mac)
[docs] def accept_response(self, response_header, content=EmptyValue, content_type=EmptyValue, accept_untrusted_content=False, localtime_offset_in_seconds=0, timestamp_skew_in_seconds=default_ts_skew_in_seconds, **auth_kw): """ Accept a response to this request. :param response_header: A `Hawk`_ ``Server-Authorization`` header such as one created by :class:`mohawk.Receiver`. :type response_header: str :param content=EmptyValue: Byte string of the response body received. :type content=EmptyValue: str :param content_type=EmptyValue: Content-Type header value of the response received. :type content_type=EmptyValue: str :param accept_untrusted_content=False: When True, allow responses that do not hash their content. Read :ref:`skipping-content-checks` to learn more. :type accept_untrusted_content=False: bool :param localtime_offset_in_seconds=0: Seconds to add to local time in case it's out of sync. :type localtime_offset_in_seconds=0: float :param timestamp_skew_in_seconds=60: Max seconds until a message expires. Upon expiry, :class:`mohawk.exc.TokenExpired` is raised. :type timestamp_skew_in_seconds=60: float .. _`Hawk`: https://github.com/hueniverse/hawk """ log.debug('accepting response {header}' .format(header=response_header)) parsed_header = parse_authorization_header(response_header) resource = Resource(ext=parsed_header.get('ext', None), content=content, content_type=content_type, # The following response attributes are # in reference to the original request, # not to the reponse header: timestamp=self.req_resource.timestamp, nonce=self.req_resource.nonce, url=self.req_resource.url, method=self.req_resource.method, app=self.req_resource.app, dlg=self.req_resource.dlg, credentials=self.credentials, seen_nonce=self.seen_nonce) self._authorize( 'response', parsed_header, resource, # Per Node lib, a responder macs the *sender's* timestamp. # It does not create its own timestamp. # I suppose a slow response could time out here. Maybe only check # mac failures, not timeouts? their_timestamp=resource.timestamp, timestamp_skew_in_seconds=timestamp_skew_in_seconds, localtime_offset_in_seconds=localtime_offset_in_seconds, accept_untrusted_content=accept_untrusted_content, **auth_kw)
def reconfigure(self, credentials): validate_credentials(credentials) self.credentials = credentials