You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/07/22 16:41:34 UTC
[12/29] allura git commit: [#7927] ticket:821 Implement CORS
middleware
[#7927] ticket:821 Implement CORS middleware
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/0c71798b
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/0c71798b
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/0c71798b
Branch: refs/heads/ib/7685
Commit: 0c71798b2dbe73f68f5912a81606b9ca73f45704
Parents: de47627
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jul 16 15:54:22 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Jul 16 17:50:54 2015 +0300
----------------------------------------------------------------------
Allura/allura/config/middleware.py | 9 ++++-
Allura/allura/lib/custom_middleware.py | 57 +++++++++++++++++++++++++++++
Allura/development.ini | 9 +++++
3 files changed, 74 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/allura/config/middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index ba07cc4..922034d 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -25,7 +25,7 @@ import tg
import tg.error
import pkg_resources
from tg import config
-from paste.deploy.converters import asbool
+from paste.deploy.converters import asbool, aslist, asint
from paste.registry import RegistryManager
from routes.middleware import RoutesMiddleware
from pylons.middleware import StatusCodeRedirect
@@ -44,6 +44,7 @@ from allura.lib.custom_middleware import AlluraTimerMiddleware
from allura.lib.custom_middleware import SSLMiddleware
from allura.lib.custom_middleware import StaticFilesMiddleware
from allura.lib.custom_middleware import CSRFMiddleware
+from allura.lib.custom_middleware import CORSMiddleware
from allura.lib.custom_middleware import LoginRedirectMiddleware
from allura.lib.custom_middleware import RememberLoginMiddleware
from allura.lib import patches
@@ -137,6 +138,12 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
# Clear cookies when the CSRF field isn't posted
if not app_conf.get('disable_csrf_protection'):
app = CSRFMiddleware(app, '_session_id')
+ if asbool(config.get('cors.enabled', False)):
+ # Handle CORS requests
+ allowed_methods = aslist(config.get('cors.methods'))
+ allowed_headers = aslist(config.get('cors.headers'))
+ cache_duration = asint(config.get('cors.cache_duration', 0))
+ app = CORSMiddleware(app, allowed_methods, allowed_headers)
# Setup the allura SOPs
app = allura_globals_middleware(app)
# Ensure http and https used per config
http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/allura/lib/custom_middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/custom_middleware.py b/Allura/allura/lib/custom_middleware.py
index 13c3db5..4eef689 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -84,6 +84,63 @@ class StaticFilesMiddleware(object):
return fileapp.FileApp(file_path, [
('Access-Control-Allow-Origin', '*')])
+class CORSMiddleware(object):
+ '''Enables Cross-Origin Resource Sharing for REST API'''
+
+ def __init__(self, app, allowed_methods, allowed_headers, cache=None):
+ self.app = app
+ self.allowed_methods = [m.upper() for m in allowed_methods]
+ self.allowed_headers = set(h.lower() for h in allowed_headers)
+ self.cache_preflight = cache or None
+
+ def __call__(self, environ, start_response):
+ is_api_request = environ.get('PATH_INFO', '').startswith('/rest/')
+ valid_cors = 'HTTP_ORIGIN' in environ
+ if not is_api_request or not valid_cors:
+ return self.app(environ, start_response)
+
+ method = environ.get('REQUEST_METHOD')
+ acrm = environ.get('HTTP_ACCESS_CONTROL_REQUEST_METHOD')
+ if method == 'OPTIONS' and acrm:
+ return self.handle_preflight_request(environ, start_response)
+ else:
+ return self.handle_simple_request(environ, start_response)
+
+ def handle_simple_request(self, environ, start_response):
+ def cors_start_response(status, headers, exc_info=None):
+ headers.extend(self.get_response_headers(preflight=False))
+ return start_response(status, headers, exc_info)
+ return self.app(environ, cors_start_response)
+
+ def handle_preflight_request(self, environ, start_response):
+ method = environ.get('HTTP_ACCESS_CONTROL_REQUEST_METHOD')
+ if method not in self.allowed_methods:
+ return self.app(environ, start_response)
+ headers = self.get_access_control_request_headers(environ)
+ if not headers.issubset(self.allowed_headers):
+ return self.app(environ, start_response)
+ r = exc.HTTPOk(headers=self.get_response_headers(preflight=True))
+ return r(environ, start_response)
+
+ def get_response_headers(self, preflight=False):
+ headers = [('Access-Control-Allow-Origin', '*')]
+ if preflight:
+ ac_methods = ', '.join(self.allowed_methods)
+ ac_headers = ', '.join(self.allowed_headers)
+ headers.extend([
+ ('Access-Control-Allow-Methods', ac_methods),
+ ('Access-Control-Allow-Headers', ac_headers),
+ ])
+ if self.cache_preflight:
+ headers.append(
+ ('Access-Control-Max-Age', self.cache_preflight)
+ )
+ return headers
+
+ def get_access_control_request_headers(self, environ):
+ headers = environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', '')
+ return set(h.strip().lower() for h in headers.split(',') if h.strip())
+
class LoginRedirectMiddleware(object):
http://git-wip-us.apache.org/repos/asf/allura/blob/0c71798b/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index 02d3e9f..934ea67 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -216,6 +216,15 @@ webhook.repo_push.limit = 30
; `WebhookSender.triggered_by`) and values are actual limits (default=3), e.g.:
webhook.repo_push.max_hooks = {"git": 3, "hg": 3, "svn": 3}
+;; Allow Cross-Origin Resource Sharing (CORS) requests to the REST API
+; disabled by default, uncomment the following options to enable:
+;cors.enabled = true
+;cors.methods = GET HEAD POST PUT DELETE
+;cors.headers = Authorization Accept Content-Type
+; Allow clients to cache preflight responses for N seconds
+; Set to 0 or remove completely to disable
+;cors.cache_duration = 86400
+
; Additional fields for admin project/user search
; Note: whitespace after comma is important!
;search.project.additional_search_fields = private, url, title