[cors] Fix CORS headers generators
authorChristophe de Vienne <christophe@unlish.com>
Mon, 15 Sep 2014 17:24:18 +0200
changeset 9989 cfb6e9dab902
parent 9988 623707a0c404
child 9991 3e7f1e9f3adc
[cors] Fix CORS headers generators The Access-Control-Allow-* and Access-Control-Expose-Headers are response headers, not request headers. Hence, we need generators for them. Closes #4412575.
web/http_headers.py
web/test/unittest_http_headers.py
--- a/web/http_headers.py	Tue Sep 09 22:14:20 2014 +0200
+++ b/web/http_headers.py	Mon Sep 15 17:24:18 2014 +0200
@@ -445,6 +445,21 @@
             l.append('%s=%s' % (k, v))
     return ";".join(l)
 
+def generateTrueFalse(value):
+    """
+    Return 'true' or 'false' depending on the value.
+
+    *   'true' values are `True`, `1`, `"true"`
+    *   'false' values are `False`, `0`, `"false"`
+
+    """
+    if (value in (True, 1) or
+            isinstance(value, basestring) and value.lower() == 'true'):
+        return 'true'
+    if (value in (False, 0) or
+            isinstance(value, basestring) and value.lower() == 'false'):
+        return 'false'
+    raise ValueError("Invalid true/false header value: %s" % value)
 
 class MimeType(object):
     def fromString(klass, mimeTypeString):
@@ -1483,12 +1498,8 @@
     'Accept-Charset': (tokenize, listParser(parseAcceptQvalue), dict, addDefaultCharset),
     'Accept-Encoding': (tokenize, listParser(parseAcceptQvalue), dict, addDefaultEncoding),
     'Accept-Language': (tokenize, listParser(parseAcceptQvalue), dict),
-    'Access-Control-Allow-Origin': (last, parseAllowOrigin,),
-    'Access-Control-Allow-Credentials': (last, parseAllowCreds,),
-    'Access-Control-Allow-Methods': (tokenize, listParser(parseHTTPMethod), list),
     'Access-Control-Request-Method': (parseHTTPMethod, ),
     'Access-Control-Request-Headers': (filterTokens, ),
-    'Access-Control-Expose-Headers': (filterTokens, ),
     'Authorization': (last, parseAuthorization),
     'Cookie': (parseCookie,),
     'Expect': (tokenize, listParser(parseExpect), dict),
@@ -1515,8 +1526,6 @@
                         listGenerator(generateAcceptQvalue), singleHeader),
     'Accept-Language': (iteritems, listGenerator(generateAcceptQvalue), singleHeader),
     'Access-Control-Request-Method': (unique, str, singleHeader, ),
-    'Access-Control-Expose-Headers': (listGenerator(str), ),
-    'Access-Control-Allow-Headers': (listGenerator(str), ),
     'Authorization': (generateAuthorization,), # what is "credentials"
     'Cookie': (generateCookie, singleHeader),
     'Expect': (iteritems, listGenerator(generateExpect), singleHeader),
@@ -1538,6 +1547,11 @@
 
 parser_response_headers = {
     'Accept-Ranges': (tokenize, filterTokens),
+    'Access-Control-Allow-Origin': (last, parseAllowOrigin,),
+    'Access-Control-Allow-Credentials': (last, parseAllowCreds,),
+    'Access-Control-Allow-Methods': (tokenize, listParser(parseHTTPMethod), list),
+    'Access-Control-Allow-Headers': (listGenerator(str), ),
+    'Access-Control-Expose-Headers': (filterTokens, ),
     'Age': (last, int),
     'ETag': (tokenize, ETag.parse),
     'Location': (last,), # TODO: URI object?
@@ -1553,6 +1567,11 @@
 
 generator_response_headers = {
     'Accept-Ranges': (generateList, singleHeader),
+    'Access-Control-Allow-Origin': (unique, str, singleHeader),
+    'Access-Control-Allow-Credentials': (generateTrueFalse, singleHeader),
+    'Access-Control-Allow-Headers': (set, generateList, singleHeader),
+    'Access-Control-Allow-Methods': (set, generateList, singleHeader),
+    'Access-Control-Expose-Headers': (set, generateList, singleHeader),
     'Age': (unique, str, singleHeader),
     'ETag': (ETag.generate, singleHeader),
     'Location': (unique, str, singleHeader),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_http_headers.py	Mon Sep 15 17:24:18 2014 +0200
@@ -0,0 +1,14 @@
+import unittest
+
+from cubicweb.web import http_headers
+
+
+class TestGenerators(unittest.TestCase):
+    def test_generate_true_false(self):
+        for v in (True, 1, 'true', 'True', 'TRUE'):
+            self.assertEqual('true', http_headers.generateTrueFalse(v))
+        for v in (False, 0, 'false', 'False', 'FALSE'):
+            self.assertEqual('false', http_headers.generateTrueFalse(v))
+
+        with self.assertRaises(ValueError):
+            http_headers.generateTrueFalse('any value')