wsgicors-0.4.1/0000775000175000017500000000000012504100413014152 5ustar normannorman00000000000000wsgicors-0.4.1/MANIFEST.in0000664000175000017500000000004712503521252015720 0ustar normannorman00000000000000include *.rst include test*.py LICENSE wsgicors-0.4.1/wsgicors.py0000664000175000017500000001263112503520414016375 0ustar normannorman00000000000000# -*- encoding: utf-8 -*- # # This file is part of wsgicors # # wsgicors is a WSGI middleware that answers CORS preflight requests # # copyright 2014-2015 Norman Krämer # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import fnmatch from functools import reduce class CORS(object): "WSGI middleware allowing CORS requests to succeed" def __init__(self, application, cfg=None, **kw): if kw: # direct config self.policy = "direct" else: # via configfile, paster factory for instance cfg = cfg or {} self.policy = cfg.get("policy", "deny") kw = {} prefix = self.policy + "_" for k, v in filter(lambda key_value: key_value[0].startswith(prefix), cfg.items()): kw[k.split(prefix)[-1]] = v # copy or * or a space separated list of hostnames, possibly with filename wildcards "*" and "?" self.pol_origin = kw.get("origin", "") if self.pol_origin not in ("copy", "*"): self.match = list(filter(lambda x: x != "*", map(lambda x: x.strip(), self.pol_origin.split(" ")))) else: self.match = [] # copy or * or a space separated list of hostnames, possibly with filename wildcards "*" and "?" self.pol_origin = kw.get("origin", "") self.pol_methods = kw.get("methods", "") # * or list of methods self.pol_headers = kw.get("headers", "") # * or list of headers self.pol_credentials = kw.get("credentials", "false") # true or false self.pol_maxage = kw.get("maxage", "") # in seconds self.application = application def __call__(self, environ, start_response): def matchpattern(accu, pattern, host): return accu or fnmatch.fnmatch(host, pattern) def matchlist(origin, allowed_origins): return reduce(lambda accu, x: matchpattern(accu, x, origin.lower()), allowed_origins, False) if 'OPTIONS' == environ['REQUEST_METHOD']: resp = [] if self.policy == "deny": pass else: origin = None methods = None headers = None credentials = None maxage = None orig = environ.get("HTTP_ORIGIN", None) if orig and self.match: if matchlist(orig, self.match): origin = orig elif self.pol_origin == "copy": origin = orig elif self.pol_origin: origin = self.pol_origin if self.pol_methods == "*": methods = environ.get("HTTP_ACCESS_CONTROL_REQUEST_METHOD", None) elif self.pol_methods: methods = self.pol_methods if self.pol_headers == "*": headers = environ.get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS", None) elif self.pol_headers: headers = self.pol_headers if self.pol_credentials == "true": credentials = "true" if self.pol_maxage: maxage = self.pol_maxage if origin: resp.append(('Access-Control-Allow-Origin', origin)) if methods: resp.append(('Access-Control-Allow-Methods', methods)) if headers: resp.append(('Access-Control-Allow-Headers', headers)) if credentials: resp.append(('Access-Control-Allow-Credentials', credentials)) if maxage: resp.append(('Access-Control-Max-Age', maxage)) status = '204 OK' start_response(status, resp) return [] orig = environ.get("HTTP_ORIGIN", None) if orig and self.policy != "deny": def custom_start_response(status, headers, exc_info=None): origin = None if orig and self.match and matchlist(orig, self.match): origin = orig elif self.pol_origin == "copy": origin = orig elif self.pol_origin == "*": origin = "*" elif self.pol_origin == orig: origin = orig if self.pol_credentials == 'true' and self.pol_origin == "*": # for credentialed access '*' are ignored in origin origin = orig if origin: headers.append(('Access-Control-Allow-Origin', origin)) if self.pol_credentials == 'true': headers.append(('Access-Control-Allow-Credentials', 'true')) return start_response(status, headers, exc_info) else: custom_start_response = start_response return self.application(environ, custom_start_response) def make_middleware(app, cfg=None, **kw): cfg = (cfg or {}).copy() cfg.update(kw) app = CORS(app, cfg) return app wsgicors-0.4.1/PKG-INFO0000664000175000017500000001274012504100413015253 0ustar normannorman00000000000000Metadata-Version: 1.1 Name: wsgicors Version: 0.4.1 Summary: WSGI for Cross Origin Resource Sharing (CORS) Home-page: https://github.com/may-day/wsgicors Author: Norman Krämer Author-email: kraemer.norman@googlemail.com License: Apache Software License 2.0 Description: wsgicors |buildstatus| ======== .. |buildstatus| image:: https://travis-ci.org/may-day/wsgicors.svg?branch=master This is a WSGI middleware that answers CORS preflight requests and adds the needed header to the response. For CORS see: http://www.w3.org/TR/cors/ Usage ----- Either plug it in programmatically as in this pyramid example: .. code:: python def app(global_config, **settings): """ This function returns a WSGI application. It is usually called by the PasteDeploy framework during ``paster serve``. """ def get_root(request): return {} config = Configurator(root_factory=get_root, settings=settings) config.begin() # whatever it takes to config your app goes here config.end() from wsgicors import CORS return CORS(config.make_wsgi_app(), headers="*", methods="*", maxage="180", origin="*") or plug it into your wsgi pipeline via paste ini to let it serve by waitress for instance: :: [app:myapp] use = egg:mysuperapp#app ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 [pipeline:main] pipeline = cors myapp [filter:cors] use = egg:wsgicors#middleware policy=free free_origin=copy free_headers=* free_methods=* free_maxage=180 policy=subdom subdom_origin=example.com example2.com *.example.com subdom_headers=* subdom_methods=* subdom_maxage=180 Keywords are: - ``origin`` - ``headers`` - ``methods`` - ``credentials`` - ``maxage`` for ``origin``: - use ``copy`` which will copy whatever origin the request comes from - a space separated list of hostnames - they can also contain wildcards like ``*`` or ``?`` (fnmatch lib is used for matching). If a match is found the original host is returned. - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``headers``: - use ``*`` which will allow whatever header is asked for - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``methods``: - use ``*`` which will allow whatever method is asked for - any other literal will be be copied verbatim (like ``POST, PATCH, PUT, DELETE`` for instance) for ``credentials``: - use ``true`` - anything else will be ignored (that is no response header for ``Access-Control-Allow-Credentials`` is sent) for ``maxage``: - give the number of seconds the answer can be used by a client, anything nonempty will be copied verbatim As can be seen in the example above, a policy needs to be created with the ``policy`` keyword. The options need then be prefixed with the policy name and a ``_``. Changes ======= Version 0.4.1 ------------- - py3 utf-8 related setup fixes Version 0.4 ----------- - python3 compatibility Version 0.3 ----------- - ``origin`` now takes space separated list of hostnames. They can be filename patterns like \*.domain.tld Version 0.2 ----------- - Access-Control-Allow-Credentials is now returned in the actual reponse if specified by policy Credits ======= “wsgicors” is written and maintained by Norman Krämer. Contributors ------------ The following people contributed directly or indirectly to this project: - `Julien De Vos `_ - `Ryan Shaw `_ - `David Douard `_ Please add yourself here when you submit your first pull request. Keywords: wsgi,cors Platform: UNKNOWN Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Internet :: WWW/HTTP :: WSGI wsgicors-0.4.1/setup.cfg0000664000175000017500000000011312504100413015766 0ustar normannorman00000000000000[easy_install] [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 wsgicors-0.4.1/wsgicors.egg-info/0000775000175000017500000000000012504100413017504 5ustar normannorman00000000000000wsgicors-0.4.1/wsgicors.egg-info/PKG-INFO0000664000175000017500000001274012504100413020605 0ustar normannorman00000000000000Metadata-Version: 1.1 Name: wsgicors Version: 0.4.1 Summary: WSGI for Cross Origin Resource Sharing (CORS) Home-page: https://github.com/may-day/wsgicors Author: Norman Krämer Author-email: kraemer.norman@googlemail.com License: Apache Software License 2.0 Description: wsgicors |buildstatus| ======== .. |buildstatus| image:: https://travis-ci.org/may-day/wsgicors.svg?branch=master This is a WSGI middleware that answers CORS preflight requests and adds the needed header to the response. For CORS see: http://www.w3.org/TR/cors/ Usage ----- Either plug it in programmatically as in this pyramid example: .. code:: python def app(global_config, **settings): """ This function returns a WSGI application. It is usually called by the PasteDeploy framework during ``paster serve``. """ def get_root(request): return {} config = Configurator(root_factory=get_root, settings=settings) config.begin() # whatever it takes to config your app goes here config.end() from wsgicors import CORS return CORS(config.make_wsgi_app(), headers="*", methods="*", maxage="180", origin="*") or plug it into your wsgi pipeline via paste ini to let it serve by waitress for instance: :: [app:myapp] use = egg:mysuperapp#app ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 [pipeline:main] pipeline = cors myapp [filter:cors] use = egg:wsgicors#middleware policy=free free_origin=copy free_headers=* free_methods=* free_maxage=180 policy=subdom subdom_origin=example.com example2.com *.example.com subdom_headers=* subdom_methods=* subdom_maxage=180 Keywords are: - ``origin`` - ``headers`` - ``methods`` - ``credentials`` - ``maxage`` for ``origin``: - use ``copy`` which will copy whatever origin the request comes from - a space separated list of hostnames - they can also contain wildcards like ``*`` or ``?`` (fnmatch lib is used for matching). If a match is found the original host is returned. - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``headers``: - use ``*`` which will allow whatever header is asked for - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``methods``: - use ``*`` which will allow whatever method is asked for - any other literal will be be copied verbatim (like ``POST, PATCH, PUT, DELETE`` for instance) for ``credentials``: - use ``true`` - anything else will be ignored (that is no response header for ``Access-Control-Allow-Credentials`` is sent) for ``maxage``: - give the number of seconds the answer can be used by a client, anything nonempty will be copied verbatim As can be seen in the example above, a policy needs to be created with the ``policy`` keyword. The options need then be prefixed with the policy name and a ``_``. Changes ======= Version 0.4.1 ------------- - py3 utf-8 related setup fixes Version 0.4 ----------- - python3 compatibility Version 0.3 ----------- - ``origin`` now takes space separated list of hostnames. They can be filename patterns like \*.domain.tld Version 0.2 ----------- - Access-Control-Allow-Credentials is now returned in the actual reponse if specified by policy Credits ======= “wsgicors” is written and maintained by Norman Krämer. Contributors ------------ The following people contributed directly or indirectly to this project: - `Julien De Vos `_ - `Ryan Shaw `_ - `David Douard `_ Please add yourself here when you submit your first pull request. Keywords: wsgi,cors Platform: UNKNOWN Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Internet :: WWW/HTTP :: WSGI wsgicors-0.4.1/wsgicors.egg-info/SOURCES.txt0000664000175000017500000000040112504100413021363 0ustar normannorman00000000000000AUTHORS.rst CHANGES.rst MANIFEST.in README.rst setup.cfg setup.py test-wsgicors.py wsgicors.py wsgicors.egg-info/PKG-INFO wsgicors.egg-info/SOURCES.txt wsgicors.egg-info/dependency_links.txt wsgicors.egg-info/entry_points.txt wsgicors.egg-info/top_level.txtwsgicors-0.4.1/wsgicors.egg-info/top_level.txt0000664000175000017500000000001112504100413022226 0ustar normannorman00000000000000wsgicors wsgicors-0.4.1/wsgicors.egg-info/entry_points.txt0000664000175000017500000000012412504100413022777 0ustar normannorman00000000000000 [paste.filter_app_factory] middleware = wsgicors:make_middleware wsgicors-0.4.1/wsgicors.egg-info/dependency_links.txt0000664000175000017500000000000112504100413023552 0ustar normannorman00000000000000 wsgicors-0.4.1/CHANGES.rst0000664000175000017500000000061212504077777016005 0ustar normannorman00000000000000Changes ======= Version 0.4.1 ------------- - py3 utf-8 related setup fixes Version 0.4 ----------- - python3 compatibility Version 0.3 ----------- - ``origin`` now takes space separated list of hostnames. They can be filename patterns like \*.domain.tld Version 0.2 ----------- - Access-Control-Allow-Credentials is now returned in the actual reponse if specified by policy wsgicors-0.4.1/AUTHORS.rst0000664000175000017500000000060412503521772016047 0ustar normannorman00000000000000Credits ======= “wsgicors” is written and maintained by Norman Krämer. Contributors ------------ The following people contributed directly or indirectly to this project: - `Julien De Vos `_ - `Ryan Shaw `_ - `David Douard `_ Please add yourself here when you submit your first pull request. wsgicors-0.4.1/test-wsgicors.py0000664000175000017500000001665012503520414017357 0ustar normannorman00000000000000# -*- encoding: utf-8 -*- # # This file is part of wsgicors # # wsgicors is a WSGI middleware that answers CORS preflight requests # # copyright 2014-2015 Norman Krämer # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from webob import Request, Response from wsgicors import make_middleware as mw from nose import with_setup deny = {"policy":"deny"} free = {"policy":"pol", "pol_origin":"*", "pol_methods":"*", "pol_headers":"*", "pol_credentials":"true", "pol_maxage":"100" } wildcard = {"policy":"pol", "pol_origin":"example.com example?.com *.example.com", "pol_methods":"*", "pol_headers":"*", "pol_credentials":"true", "pol_maxage":"100" } free_nocred = {"policy":"pol", "pol_origin":"*", "pol_methods":"*", "pol_headers":"*", "pol_credentials":"false", "pol_maxage":"100" } verbatim = {"policy":"pol", "pol_origin":"example.com", "pol_methods":"put,delete", "pol_headers":"header1,header2", "pol_credentials":"true", "pol_maxage":"100" } post2 = post = preflight = None def setup(): global preflight, deniedpreflight, allowedpreflight, post, post2, post3 preflight = Request.blank("/") preflight.method="OPTIONS" preflight.headers["Access-Control-Request-Method"] = "post" preflight.headers["Access-Control-Request-Headers"] = "*" deniedpreflight = Request.blank("/") deniedpreflight.method="OPTIONS" deniedpreflight.headers["Access-Control-Request-Method"] = "post" deniedpreflight.headers["Access-Control-Request-Headers"] = "*" deniedpreflight.headers["Origin"] = "somedomain.com" allowedpreflight = Request.blank("/") allowedpreflight.method="OPTIONS" allowedpreflight.headers["Access-Control-Request-Method"] = "post" allowedpreflight.headers["Access-Control-Request-Headers"] = "*" allowedpreflight.headers["Origin"] = "sub.example.com" post = Request.blank("/") post.method="POST" post.headers["Origin"] = "example.com" post2 = Request.blank("/") post2.method="POST" post2.headers["Origin"] = "example2.com" post3 = Request.blank("/") post3.method="POST" post3.headers["Origin"] = "sub.example.com" @with_setup(setup) def testdeny(): corsed = mw(Response(), deny) res = preflight.get_response(corsed) assert "Access-Control-Allow-Origin" not in res.headers assert "Access-Control-Allow-Credentials" not in res.headers assert "Access-Control-Allow-Methods" not in res.headers assert "Access-Control-Allow-Headers" not in res.headers assert "Access-Control-Max-Age" not in res.headers res = post.get_response(corsed) assert "Access-Control-Allow-Origin" not in res.headers assert "Access-Control-Allow-Credentials" not in res.headers @with_setup(setup) def testfree(): corsed = mw(Response(), free) res = preflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "*" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" assert res.headers.get("Access-Control-Allow-Methods", "") == "post" assert res.headers.get("Access-Control-Allow-Headers", "") == "*" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = post.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" @with_setup(setup) def testwildcard(): corsed = mw(Response(), wildcard) res = deniedpreflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" assert res.headers.get("Access-Control-Allow-Methods", "") == "post" assert res.headers.get("Access-Control-Allow-Headers", "") == "*" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = allowedpreflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "sub.example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" assert res.headers.get("Access-Control-Allow-Methods", "") == "post" assert res.headers.get("Access-Control-Allow-Headers", "") == "*" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = post.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" res = post3.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "sub.example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" @with_setup(setup) def testfree_nocred(): """ similar to free, but the actual request will be answered with a '*' for allowed origin """ corsed = mw(Response(), free_nocred) res = preflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "*" assert res.headers.get("Access-Control-Allow-Credentials", None) == None assert res.headers.get("Access-Control-Allow-Methods", "") == "post" assert res.headers.get("Access-Control-Allow-Headers", "") == "*" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = post.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "*" assert res.headers.get("Access-Control-Allow-Credentials", None) == None @with_setup(setup) def testverbatim(): corsed = mw(Response(), verbatim) res = preflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" assert res.headers.get("Access-Control-Allow-Methods", "") == "put,delete" assert res.headers.get("Access-Control-Allow-Headers", "") == "header1,header2" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = post.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" @with_setup(setup) def test_req_origin_no_match(): "sending a post from a disallowed host => no allow headers will be returned" corsed = mw(Response(), verbatim) res = preflight.get_response(corsed) assert res.headers.get("Access-Control-Allow-Origin", "") == "example.com" assert res.headers.get("Access-Control-Allow-Credentials", "") == "true" assert res.headers.get("Access-Control-Allow-Methods", "") == "put,delete" assert res.headers.get("Access-Control-Allow-Headers", "") == "header1,header2" assert res.headers.get("Access-Control-Max-Age", "0") == "100" res = post2.get_response(corsed) assert "Access-Control-Allow-Origin" not in res.headers assert "Access-Control-Allow-Credentials" not in res.headers wsgicors-0.4.1/setup.py0000664000175000017500000000303512504077711015703 0ustar normannorman00000000000000#-*- coding:utf-8 -*- from setuptools import setup import sys, os import codecs here = os.path.abspath(os.path.dirname(__file__)) def readfile(fname): return codecs.open(os.path.join(here, fname), encoding='utf-8').read() version = '0.4.1' README = readfile('README.rst') CHANGES = readfile('CHANGES.rst') AUTHORS = readfile('AUTHORS.rst') # hack, or test wont run on py2.7 try: import multiprocessing import logging except: pass setup(name='wsgicors', version=version, description="WSGI for Cross Origin Resource Sharing (CORS)", long_description=README + '\n\n' + CHANGES + '\n\n' + AUTHORS, classifiers = [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Internet :: WWW/HTTP :: WSGI" ], keywords=["wsgi", "cors"], author='Norman Krämer', author_email='kraemer.norman@googlemail.com', url="https://github.com/may-day/wsgicors", license='Apache Software License 2.0', py_modules=["wsgicors"], tests_require = [ 'nose', 'nose-testconfig', 'webob' ], test_suite = 'nose.collector', entry_points = """ [paste.filter_app_factory] middleware = wsgicors:make_middleware """ ) wsgicors-0.4.1/README.rst0000664000175000017500000000540312444134112015651 0ustar normannorman00000000000000wsgicors |buildstatus| ======== .. |buildstatus| image:: https://travis-ci.org/may-day/wsgicors.svg?branch=master This is a WSGI middleware that answers CORS preflight requests and adds the needed header to the response. For CORS see: http://www.w3.org/TR/cors/ Usage ----- Either plug it in programmatically as in this pyramid example: .. code:: python def app(global_config, **settings): """ This function returns a WSGI application. It is usually called by the PasteDeploy framework during ``paster serve``. """ def get_root(request): return {} config = Configurator(root_factory=get_root, settings=settings) config.begin() # whatever it takes to config your app goes here config.end() from wsgicors import CORS return CORS(config.make_wsgi_app(), headers="*", methods="*", maxage="180", origin="*") or plug it into your wsgi pipeline via paste ini to let it serve by waitress for instance: :: [app:myapp] use = egg:mysuperapp#app ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 [pipeline:main] pipeline = cors myapp [filter:cors] use = egg:wsgicors#middleware policy=free free_origin=copy free_headers=* free_methods=* free_maxage=180 policy=subdom subdom_origin=example.com example2.com *.example.com subdom_headers=* subdom_methods=* subdom_maxage=180 Keywords are: - ``origin`` - ``headers`` - ``methods`` - ``credentials`` - ``maxage`` for ``origin``: - use ``copy`` which will copy whatever origin the request comes from - a space separated list of hostnames - they can also contain wildcards like ``*`` or ``?`` (fnmatch lib is used for matching). If a match is found the original host is returned. - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``headers``: - use ``*`` which will allow whatever header is asked for - any other literal will be be copied verbatim (like ``*`` for instance to allow every source) for ``methods``: - use ``*`` which will allow whatever method is asked for - any other literal will be be copied verbatim (like ``POST, PATCH, PUT, DELETE`` for instance) for ``credentials``: - use ``true`` - anything else will be ignored (that is no response header for ``Access-Control-Allow-Credentials`` is sent) for ``maxage``: - give the number of seconds the answer can be used by a client, anything nonempty will be copied verbatim As can be seen in the example above, a policy needs to be created with the ``policy`` keyword. The options need then be prefixed with the policy name and a ``_``.