# HG changeset patch # User Christophe de Vienne # Date 1410794658 -7200 # Node ID cfb6e9dab902036323444cabc97735f3e80eb484 # Parent 623707a0c404737dd6d7809438301432445725bb [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. diff -r 623707a0c404 -r cfb6e9dab902 web/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), diff -r 623707a0c404 -r cfb6e9dab902 web/test/unittest_http_headers.py --- /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')