[PATCH cube signedrequest V2] Support alternative to Date header

Laurent Wouters lwouters at cenotelie.fr
Tue Jun 25 09:42:15 CEST 2019


# HG changeset patch
# User Laurent Wouters <lwouters at cenotelie.fr>
# Date 1561448446 -7200
#      Tue Jun 25 09:40:46 2019 +0200
# Node ID 2b5cca1b75afbe0492c93c35283fb537e3203713
# Parent  9cf3d9ab91e3fd3b3dda51991e73859cbce2abda
Support alternative to Date header

The current protocol for signed request requires the use of the Date HTTP
header. Although this works fine for clients that have full control over the
HTTP headers they send, this is not working in the context of web browser where
the Date HTTP headers are forbidden to be programmatically set (and therefore
used in any meaningful way)
https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name

In general, this change enables the specification of a prioritized list of
alternative for headers. In particular for the Date header, this change
specifies a the list ['X-Cubicweb-Date', 'Date'] as an alternative to the Date
header; meaning that when looking for the Date header, one should first look
at the X-Cubicweb-Date header, and then if not present at the Date header. Doing
so, it should be possible to emit signed requests from the context of a browser
by specifying a X-Cubicweb-Date header, overriding the Date header that the
browser may or may not set by itself.

diff -r 9cf3d9ab91e3 -r 2b5cca1b75af cubicweb_signedrequest/tools.py
--- a/cubicweb_signedrequest/tools.py	Wed Mar 06 15:09:48 2019 +0000
+++ b/cubicweb_signedrequest/tools.py	Tue Jun 25 09:40:46 2019 +0200
@@ -48,6 +48,29 @@
 log = logging.getLogger(__name__)
 
 HEADERS_TO_SIGN = ('Content-MD5', 'Content-Type', 'Date')
+ALTERNATE_HEADERS = {'Date': ['X-Cubicweb-Date', 'Date']}
+
+
+def get_replaceable_header_value(request, header_name, default=None):
+    """
+    Get the value for a header, looking at prioritized alternatives as required
+    :param request: The request
+    :param header_name: The name of the header
+    :param default: The default value in case the header is not specified
+    :return: The value of the header, or its alternatives
+    """
+    alternates = ALTERNATE_HEADERS[header_name]
+    if alternates is not None:
+        # we have a list of prioritized headers
+        for header_name in alternates:
+            value = request.get_header(header_name)
+            if value is not None:
+                return value
+        return default
+    else:
+        # default behavior
+        value = request.get_header(header_name)
+        return value or default
 
 
 def hash_content(content):
@@ -73,7 +96,7 @@
                   forged using the token's secret key to authenticate
                   the user linked with the AuthToken
     """
-    header = request.get_header('Authorization', None)
+    header = get_replaceable_header_value(request, 'Authorization', None)
     if header is None:
         log.debug('SIGNED REQUEST: error header is none')
         return
@@ -87,12 +110,12 @@
         log.debug('SIGNED REQUEST: method is not Cubicweb')
         return
     if request.http_method() != 'GET':
-        if content_md5 != request.get_header('Content-MD5'):
+        if content_md5 != get_replaceable_header_value(request, 'Content-MD5'):
             log.error('SIGNED REQUEST: wrong md5, %s != %s' % (
                 content_md5,
-                request.get_header('Content-MD5')))
+                get_replaceable_header_value(request, 'Content-MD5')))
             raise AuthenticationError()
-    date_header = request.get_header('Date')
+    date_header = get_replaceable_header_value(request, 'Date')
     if date_header is None:
         raise AuthenticationError()
     try:
@@ -125,7 +148,7 @@
         headers = HEADERS_TO_SIGN
     if url is None:
         url = request.url
-    get_header = lambda field: request.get_header(field, '')  # noqa
+    get_header = lambda field: get_replaceable_header_value(request, field, '')  # noqa
     return (request.http_method() + url +
             ''.join(map(get_header, headers))).encode('utf-8')
 



More information about the cubicweb-devel mailing list