-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
[ | ||
{ | ||
"key": "group_6466cc3ca925a", | ||
"title": "Proxy Settings", | ||
"fields": [ | ||
{ | ||
"key": "field_6466cc3df4887", | ||
"label": "Providers", | ||
"name": "providers", | ||
"aria-label": "", | ||
"type": "repeater", | ||
"instructions": "Enter details for the services you wish to proxy.", | ||
"required": 0, | ||
"conditional_logic": 0, | ||
"wrapper": { | ||
"width": "", | ||
"class": "", | ||
"id": "" | ||
}, | ||
"show_in_graphql": 1, | ||
"layout": "row", | ||
"pagination": 0, | ||
"min": 0, | ||
"max": 0, | ||
"collapsed": "", | ||
"button_label": "Add Provider", | ||
"rows_per_page": 20, | ||
"sub_fields": [ | ||
{ | ||
"key": "field_6466cc64f4888", | ||
"label": "Name", | ||
"name": "name", | ||
"aria-label": "", | ||
"type": "text", | ||
"instructions": "The name of the provider. Used in frontend code for reference.", | ||
"required": 0, | ||
"conditional_logic": 0, | ||
"wrapper": { | ||
"width": "", | ||
"class": "", | ||
"id": "" | ||
}, | ||
"show_in_graphql": 1, | ||
"default_value": "", | ||
"maxlength": "", | ||
"placeholder": "", | ||
"prepend": "", | ||
"append": "", | ||
"parent_repeater": "field_6466cc3df4887" | ||
}, | ||
{ | ||
"key": "field_6466cc6af4889", | ||
"label": "Base URL", | ||
"name": "base_url", | ||
"aria-label": "", | ||
"type": "url", | ||
"instructions": "The base URL of the API you are trying to proxy. Tip: leave off the trailing slash.", | ||
"required": 0, | ||
"conditional_logic": 0, | ||
"wrapper": { | ||
"width": "", | ||
"class": "", | ||
"id": "" | ||
}, | ||
"show_in_graphql": 1, | ||
"default_value": "", | ||
"placeholder": "", | ||
"parent_repeater": "field_6466cc3df4887" | ||
}, | ||
{ | ||
"key": "field_6466cc74f488a", | ||
"label": "Authorization header", | ||
"name": "authorization_header", | ||
"aria-label": "", | ||
"type": "text", | ||
"instructions": "The HTTP Authorization header to add to the request. Often this will be of the format `Bearer xxxxxxxxx`", | ||
"required": 0, | ||
"conditional_logic": 0, | ||
"wrapper": { | ||
"width": "", | ||
"class": "", | ||
"id": "" | ||
}, | ||
"show_in_graphql": 1, | ||
"default_value": "", | ||
"maxlength": "", | ||
"placeholder": "", | ||
"prepend": "", | ||
"append": "", | ||
"parent_repeater": "field_6466cc3df4887" | ||
} | ||
] | ||
}, | ||
{ | ||
"key": "field_6466ccb3f488b", | ||
"label": "Domain restricted", | ||
"name": "domain_restricted", | ||
"aria-label": "", | ||
"type": "true_false", | ||
"instructions": "When true, only frontend requests from the Site and WordPress URLs will be allowed. When false, requests from all domains will be allowed. This is a security issue, so only have this false during when testing from localhost.", | ||
"required": 0, | ||
"conditional_logic": 0, | ||
"wrapper": { | ||
"width": "", | ||
"class": "", | ||
"id": "" | ||
}, | ||
"show_in_graphql": 1, | ||
"message": "Restrict access to the Site Address and WordPress Address", | ||
"default_value": 1, | ||
"ui_on_text": "", | ||
"ui_off_text": "", | ||
"ui": 1 | ||
} | ||
], | ||
"location": [ | ||
[ | ||
{ | ||
"param": "options_page", | ||
"operator": "==", | ||
"value": "proxy-settings" | ||
} | ||
] | ||
], | ||
"menu_order": 0, | ||
"position": "normal", | ||
"style": "default", | ||
"label_placement": "top", | ||
"instruction_placement": "label", | ||
"hide_on_screen": "", | ||
"active": true, | ||
"description": "", | ||
"show_in_rest": 0, | ||
"show_in_graphql": 0, | ||
"graphql_field_name": "proxySettings", | ||
"map_graphql_types_from_location_rules": 0, | ||
"graphql_types": "" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
<?php | ||
/** | ||
* This file handles the built in proxy that the frontend can use to bypass cors | ||
* | ||
* @package fuxt-backend | ||
*/ | ||
|
||
/* | ||
* Register custom Proxy API endpoints | ||
*/ | ||
function fuxt_proxy_init() | ||
{ | ||
// Setup new endpoint | ||
register_rest_route("fuxt/v1", "/proxy", [ | ||
// Create Subscriber | ||
[ | ||
"methods" => "POST, GET, DELETE, PATCH, PUT", | ||
"callback" => "fuxt_proxy_do_request", | ||
"permission_callback" => "fuxt_proxy_check_permission", | ||
], | ||
]); | ||
|
||
// Change headers to allow the custom headers we need | ||
add_filter( | ||
"rest_pre_serve_request", | ||
"fuxt_proxy_add_custom_cors_headers", | ||
20, | ||
4 | ||
); | ||
} | ||
add_action("rest_api_init", "fuxt_proxy_init"); | ||
|
||
/* | ||
* Check's that the request matches allowed Origin and Provider name is in ACF repeater field | ||
* Returns true || WP_Error | ||
*/ | ||
function fuxt_proxy_check_permission($request) | ||
{ | ||
// Security is be on, so check that Origin is on whitelist | ||
$domain_restricted = get_field("domain_restricted", "option"); | ||
if ( | ||
$domain_restricted && | ||
!fuxt_proxy_request_from_allowed_origin($request) | ||
) { | ||
return new WP_Error( | ||
"permission", | ||
"Your origin is not allowed to make this Proxy request" | ||
); | ||
} | ||
|
||
// Passed Origin checks, now check requested asked for an approved Provider | ||
$proxy_name = $request->get_header("Fuxt-Proxy-Name"); | ||
$provider = fuxt_proxy_get_provider("name", $proxy_name); | ||
|
||
// Allow if Provider is found | ||
if ($proxy_name && $provider) { | ||
return true; | ||
} | ||
|
||
return new WP_Error("permission", "Your requested Proxy Name is invalid"); | ||
} | ||
|
||
/* | ||
* Forwards request to allowed API, adds in Bearer token from ACF Proxy Settings field group | ||
* Returns WP_REST_Response || WP_Error | ||
*/ | ||
function fuxt_proxy_do_request($request) | ||
{ | ||
// Get bearer token from ACF field, include in request below | ||
$proxy_name = $request->get_header("Fuxt-Proxy-Name"); | ||
$proxy_endpoint = $request->get_header("Fuxt-Proxy-Endpoint"); | ||
$provider = fuxt_proxy_get_provider("name", $proxy_name); | ||
|
||
// Get any found tokens from ACF, build out full request URL | ||
$auth_header = $provider["authorization_header"] ?? ""; | ||
$url = $provider["base_url"] . $proxy_endpoint; | ||
|
||
// Encode the body to JSON if supplied | ||
$body = $request->get_json_params(); | ||
if ($body && is_array($body)) { | ||
$body = json_encode($body); | ||
} | ||
|
||
// Setup HTTP Request, pass through as much settings as we can | ||
$args = [ | ||
"headers" => [ | ||
"Authorization" => $auth_header, | ||
"Content-Type" => $request->get_header("Content-Type"), | ||
], | ||
"method" => $request->get_method(), | ||
"body" => $body ?? "", | ||
]; | ||
|
||
// Send remote request! Go Proxy Go! | ||
$response = wp_remote_request($url, $args); | ||
|
||
// Retrieve information from the $response | ||
$response_code = wp_remote_retrieve_response_code($response); | ||
$response_message = wp_remote_retrieve_response_message($response); | ||
$response_headers = wp_remote_retrieve_headers($response); | ||
$response_body = wp_remote_retrieve_body($response); | ||
|
||
// Check if response is JSON, if so then decode it so that when we send it later it's not double encoded | ||
if( fuxt_proxy_is_json($response_body) ) { | ||
$response_body = json_decode($response_body); | ||
} | ||
|
||
// Return data or error back to frontend | ||
if (!is_wp_error($response)) { | ||
return new WP_REST_Response($response_body, $response_code, (array)$response_headers); | ||
} | ||
|
||
return new WP_Error($response_code, $response_message, $response_body); | ||
} | ||
|
||
/* | ||
* This customizes the CORS headers the server will accept, allowing use of our Proxy custom headers | ||
* Returns true || false | ||
*/ | ||
function fuxt_proxy_add_custom_cors_headers($served, $result, $request, $server) | ||
{ | ||
// Abort if not a request to the Proxy endpoint | ||
if ($request->get_route() !== "/fuxt/v1/proxy") { | ||
return $served; | ||
} | ||
|
||
// Now add our headers | ||
header( | ||
"Access-Control-Allow-Headers: Fuxt-Proxy-Name, Fuxt-Proxy-Endpoint, Content-Type, Authorization" | ||
); | ||
|
||
return $served; | ||
} | ||
|
||
/* | ||
* Check that the request is from an allowed Origin. | ||
* Returns true || false | ||
*/ | ||
function fuxt_proxy_request_from_allowed_origin($request) | ||
{ | ||
$origin = get_http_origin(); | ||
|
||
// Start with site url as allowed origin. | ||
$allowed_origins = [site_url(), home_url()]; | ||
|
||
// Add fuxt home url to allowed origin. | ||
$fuxt_home_url = get_option("fuxt_home_url"); | ||
if ($fuxt_home_url) { | ||
$allowed_origins[] = $fuxt_home_url; | ||
} | ||
|
||
$allowed_origins = apply_filters("fuxt_allowed_origins", $allowed_origins); | ||
|
||
// Current request comes from an Origin that is allowed | ||
if (in_array($origin, $allowed_origins, true)) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/* | ||
* Return the Provider if found, or false. | ||
* Returns Array || false | ||
*/ | ||
function fuxt_proxy_get_provider($search_key, $search_value) | ||
{ | ||
$proxy_providers = get_field("providers", "option"); | ||
|
||
$columns = array_column($proxy_providers, $search_key); | ||
$found = array_search($search_value, $columns); | ||
|
||
// Return found Provider | ||
if (is_int($found)) { | ||
return $proxy_providers[$found]; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/* | ||
* Checks if a string is JSON | ||
* Returns Array || false | ||
*/ | ||
function fuxt_proxy_is_json($str) { | ||
$json = json_decode($str); | ||
return $json && $str != $json; | ||
} |