now = utc_now(offset_in_seconds=localtime_offset_in_seconds)
their_hash = parsed_header.get('hash', '')
their_mac = parsed_header.get('mac', '')
mac = calculate_mac(mac_type, resource, their_hash) ifnot strings_match(mac, their_mac): raise MacMismatch('MACs do not match; ours: {ours}; ' 'theirs: {theirs}'
.format(ours=mac, theirs=their_mac))
check_hash = True
if'hash'notin parsed_header: # The request did not hash its content. ifnot resource.content andnot resource.content_type: # It is acceptable to not receive a hash if there is no content # to hash.
log.debug('NOT calculating/verifying payload hash ' '(no hash in header, request body is empty)')
check_hash = False elif accept_untrusted_content: # Allow the request, even if it has content. Missing content or # content_type values will be coerced to the empty string for # hashing purposes.
log.debug('NOT calculating/verifying payload hash ' '(no hash in header, accept_untrusted_content=True)')
check_hash = False
if check_hash: ifnot their_hash:
log.info('request unexpectedly did not hash its content')
content_hash = resource.gen_content_hash()
ifnot strings_match(content_hash, their_hash): # The hash declared in the header is incorrect. # Content could have been tampered with.
log.debug('mismatched content: {content}'
.format(content=repr(resource.content)))
log.debug('mismatched content-type: {typ}'
.format(typ=repr(resource.content_type))) raise MisComputedContentHash( 'Our hash {ours} ({algo}) did not ' 'match theirs {theirs}'
.format(ours=content_hash,
theirs=their_hash,
algo=resource.credentials['algorithm']))
if resource.seen_nonce: if resource.seen_nonce(resource.credentials['id'],
parsed_header['nonce'],
parsed_header['ts']): raise AlreadyProcessed('Nonce {nonce} with timestamp {ts} ' 'has already been processed for {id}'
.format(nonce=parsed_header['nonce'],
ts=parsed_header['ts'],
id=resource.credentials['id'])) else:
log.warning('seen_nonce was None; not checking nonce. ' 'You may be vulnerable to replay attacks')
their_ts = int(their_timestamp or parsed_header['ts'])
if math.fabs(their_ts - now) > timestamp_skew_in_seconds:
message = ('token with UTC timestamp {ts} has expired; ' 'it was compared to {now}'
.format(ts=their_ts, now=now))
tsm = calculate_ts_mac(now, resource.credentials) if isinstance(tsm, six.binary_type):
tsm = tsm.decode('ascii')
www_authenticate = ('Hawk ts="{ts}", tsm="{tsm}", error="{error}"'
.format(ts=now, tsm=tsm, error=message)) raise TokenExpired(message,
localtime_in_seconds=now,
www_authenticate=www_authenticate)
log.debug('authorized OK')
def _make_header(self, resource, mac, additional_keys=None):
keys = additional_keys ifnot keys: # These are the default header keys that you'd send with a # request header. Response headers are odd because they # exclude a bunch of keys.
keys = ('id', 'ts', 'nonce', 'ext', 'app', 'dlg')
class Resource: """
Normalized request / response resource.
:param credentials:
A dict of credentials; it must have the keys:
``id``, ``key``, and ``algorithm``.
See :ref:`sending-request` for an example.
:type credentials_map: dict
:param url: Absolute URL of the request / response.
:type url: str
:param method: Method of the request / response. E.G. POST, GET
:type method: str
:param content_type=EmptyValue: content-type header value for request / response.
: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 ext=None:
An external `Hawk`_ string. IfnotNone, this value will be
signed so that the sender can trust it.
:type ext=None: str
:param app=None:
A `Hawk`_ string identifying an external application.
:type app=None: str
:param dlg=None:
A `Hawk`_ string identifying a "delegated by" value.
:type dlg=None: str
:param timestamp=utc_now():
A unix timestamp integer, in UTC
:type timestamp: int
:param nonce=None:
A string that when coupled with the timestamp will
uniquely identify this request / response.
:type nonce=None: str
:param seen_nonce=None:
A callable that returns Trueif a nonce has been seen.
See :ref:`nonce` for details.
:type seen_nonce=None: callable
if kw.keys(): raise TypeError('Unknown keyword argument(s): {0}'
.format(kw.keys()))
@property def content_hash(self): ifnot hasattr(self, '_content_hash'): raise AttributeError( 'Cannot access content_hash because it has not been generated') return self._content_hash
def gen_content_hash(self): if self.content == EmptyValue or self.content_type == EmptyValue: if self.always_hash_content: # Be really strict about allowing developers to skip content # hashing. If they get this far they may be unintentiionally # skipping it. raise MissingContent( 'payload content and/or content_type cannot be ' 'empty when always_hash_content is True')
log.debug('NOT hashing content')
self._content_hash = None else:
self._content_hash = calculate_payload_hash(
self.content, self.credentials['algorithm'],
self.content_type) return self.content_hash
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.