From edefe139326b8c59e47a36c3781d06f9ede1d048 Mon Sep 17 00:00:00 2001 From: metadaddy Date: Mon, 7 Mar 2011 10:43:17 -0800 Subject: [PATCH 1/4] Proxy copies value of inbound 'authorization' query parameter to outbound 'Authorization:' HTTP header. --- ba-simple-proxy.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ba-simple-proxy.php b/ba-simple-proxy.php index b1fa6e8..c0e25bd 100644 --- a/ba-simple-proxy.php +++ b/ba-simple-proxy.php @@ -176,6 +176,11 @@ curl_setopt( $ch, CURLOPT_COOKIE, $cookie ); } + + if ( isset($_GET['authorization']) ) { + // Set the Authorization header + curl_setopt( $ch, CURLOPT_HTTPHEADER, array("Authorization: ".$_GET['authorization'] )); + } curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); curl_setopt( $ch, CURLOPT_HEADER, true ); From 70c57b1efaa51bb588c1e720ec8b78fede9bb764 Mon Sep 17 00:00:00 2001 From: metadaddy Date: Tue, 8 Mar 2011 11:31:05 -0800 Subject: [PATCH 2/4] Added support for POSTing non-form data and arbitrary request methods --- ba-simple-proxy.php | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/ba-simple-proxy.php b/ba-simple-proxy.php index c0e25bd..0aa1029 100644 --- a/ba-simple-proxy.php +++ b/ba-simple-proxy.php @@ -158,12 +158,15 @@ } else { $ch = curl_init( $url ); - - if ( strtolower($_SERVER['REQUEST_METHOD']) == 'post' ) { - curl_setopt( $ch, CURLOPT_POST, true ); - curl_setopt( $ch, CURLOPT_POSTFIELDS, $_POST ); - } - + + // Pass on request method, regardless of what it is + curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD'] ); + + // Pass on content, regardless of request method + if ( isset($_SERVER['CONTENT_LENGTH'] ) && $_SERVER['CONTENT_LENGTH'] > 0 ) { + curl_setopt( $ch, CURLOPT_POSTFIELDS, file_get_contents("php://input") ); + } + if ( $_GET['send_cookies'] ) { $cookie = array(); foreach ( $_COOKIE as $key => $value ) { @@ -177,9 +180,17 @@ curl_setopt( $ch, CURLOPT_COOKIE, $cookie ); } + $headers = array(); if ( isset($_GET['authorization']) ) { // Set the Authorization header - curl_setopt( $ch, CURLOPT_HTTPHEADER, array("Authorization: ".$_GET['authorization'] )); + array_push($headers, "Authorization: ".$_GET['authorization'] ); + } + if ( isset($_SERVER['CONTENT_TYPE']) ) { + // Pass through the Content-Type header + array_push($headers, "Content-Type: ".$_SERVER['CONTENT_TYPE'] ); + } + if ( count($headers) > 0 ) { + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); } curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); @@ -253,7 +264,7 @@ $json = json_encode( $data ); print $jsonp_callback ? "$jsonp_callback($json)" : $json; - + } ?> From c1c5c8b8e053202603d58a76760c955cf8f791c7 Mon Sep 17 00:00:00 2001 From: metadaddy Date: Tue, 8 Mar 2011 13:23:19 -0800 Subject: [PATCH 3/4] Switched from using a query parameter for authorization to a header, so the token doesn't show up in server logs. --- ba-simple-proxy.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ba-simple-proxy.php b/ba-simple-proxy.php index 0aa1029..ac4fc08 100644 --- a/ba-simple-proxy.php +++ b/ba-simple-proxy.php @@ -165,7 +165,7 @@ // Pass on content, regardless of request method if ( isset($_SERVER['CONTENT_LENGTH'] ) && $_SERVER['CONTENT_LENGTH'] > 0 ) { curl_setopt( $ch, CURLOPT_POSTFIELDS, file_get_contents("php://input") ); - } + } if ( $_GET['send_cookies'] ) { $cookie = array(); @@ -181,9 +181,9 @@ } $headers = array(); - if ( isset($_GET['authorization']) ) { + if ( isset($_SERVER['HTTP_X_AUTHORIZATION']) ) { // Set the Authorization header - array_push($headers, "Authorization: ".$_GET['authorization'] ); + array_push($headers, "Authorization: ".$_SERVER['HTTP_X_AUTHORIZATION'] ); } if ( isset($_SERVER['CONTENT_TYPE']) ) { // Pass through the Content-Type header From 3fbca90bcd6df9e50d5f23ef226498e420134168 Mon Sep 17 00:00:00 2001 From: metadaddy Date: Fri, 11 Mar 2011 17:01:44 -0800 Subject: [PATCH 4/4] Added CORS support (see http://www.w3.org/TR/cors/) and parameterized incoming authz header --- ba-simple-proxy.php | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/ba-simple-proxy.php b/ba-simple-proxy.php index ac4fc08..0ab6450 100644 --- a/ba-simple-proxy.php +++ b/ba-simple-proxy.php @@ -132,6 +132,19 @@ // ensure that it is valid. This setting only needs to be used if either // $enable_jsonp or $enable_native are enabled. Defaults to '/.*/' which // validates all URLs. +// $authz_header - an index into the $_SERVER array locating authorization +// data which is to be proxied in the HTTP Authorization header. This is +// necessary since, in a default deployment, Apache will not pass an +// incoming Authorization header to a script. As a convention, we pass +// authorization data to the proxy in the X-Authorization header, so the +// default value is 'HTTP_X_AUTHORIZATION' +// $cors_allow_origin - a space-separated list of origins, each of the form +// https://example.com:8443, from which scripts will be allowed to access +// the proxy. See http://www.w3.org/TR/cors/ for details. +// $cors_allow_methods - HTTP methods allowed from the origins specified in +// $cors_allow_origin. Defaults to 'GET, POST, PUT, PATCH, DELETE, HEAD' +// $cors_allow_headers - HTTP headers allowed from the origins specified in +// $cors_allow_origin. Defaults to 'X-Authorization, Content-Type' // // ############################################################################ @@ -140,6 +153,12 @@ $enable_native = false; $valid_url_regex = '/.*/'; +$authz_header = 'HTTP_X_AUTHORIZATION'; + +$cors_allow_origin = null; +$cors_allow_methods = 'GET, POST, PUT, PATCH, DELETE, HEAD'; +$cors_allow_headers = 'X-Authorization, Content-Type'; + // ############################################################################ $url = $_GET['url']; @@ -157,6 +176,21 @@ $status = array( 'http_code' => 'ERROR' ); } else { + + if ( isset( $cors_allow_origin ) ) { + header( 'Access-Control-Allow-Origin: '.$cors_allow_origin ); + if ( isset( $cors_allow_methods ) ) { + header( 'Access-Control-Allow-Methods: '.$cors_allow_methods ); + } + if ( isset( $cors_allow_headers ) ) { + header( 'Access-Control-Allow-Headers: '.strtolower($cors_allow_headers) ); + } + if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) { + // We're done - don't proxy CORS OPTIONS request + exit(); + } + } + $ch = curl_init( $url ); // Pass on request method, regardless of what it is @@ -181,9 +215,9 @@ } $headers = array(); - if ( isset($_SERVER['HTTP_X_AUTHORIZATION']) ) { + if ( isset($authz_header) && isset($_SERVER[$authz_header]) ) { // Set the Authorization header - array_push($headers, "Authorization: ".$_SERVER['HTTP_X_AUTHORIZATION'] ); + array_push($headers, "Authorization: ".$_SERVER[$authz_header] ); } if ( isset($_SERVER['CONTENT_TYPE']) ) { // Pass through the Content-Type header @@ -252,7 +286,7 @@ // Set the JSON data object contents, decoding it from JSON if possible. $decoded_json = json_decode( $contents ); $data['contents'] = $decoded_json ? $decoded_json : $contents; - + // Generate appropriate content-type header. $is_xhr = strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'; header( 'Content-type: application/' . ( $is_xhr ? 'json' : 'x-javascript' ) );