first draft callback functionality

This commit is contained in:
Michael McAndrew 2018-03-12 08:56:51 +00:00
parent 9e9bae4015
commit ec3b95aabc
3 changed files with 144 additions and 0 deletions

View File

@ -0,0 +1,66 @@
<?php
function civiproxy_callback_validate_request_method($expected, $actual){
if($expected != $actual){
civiproxy_http_error("Invalid request method.", 405);
}
}
function civiproxy_callback_validate_content_type($expected, $actual){
if($expected != $actual){
civiproxy_http_error("Forbidden content type.", 403);
}
}
function civiproxy_callback_validate_body($expected, $actual, $content_type){
switch ($content_type) {
case 'application/json':
civiproxy_callback_validate_body_json($expected, $actual);
break;
case 'application/x-www-form-urlencoded':
civiproxy_callback_validate_body_xwwwformurlencoded($expected, $actual);
break;
default:
civiproxy_http_error("Forbidden content type (expecting {$expected}).", 403);
}
}
function civiproxy_callback_validate_body_json($expected, $actual) {
//TODO
}
function civiproxy_callback_validate_body_xwwwformurlencoded($expected, $actual) {
//TODO
}
function civiproxy_callback_redirect($target_path, $method) {
switch ($method) {
case 'POST':
civiproxy_callback_redirect_post($target_path);
break;
}
exit;
}
// Change the URL, forward the body. Respond with the response
function civiproxy_callback_redirect_post($target_path) {
global $target_civicrm;
$target_url = "$target_civicrm/{$target_path}";
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents('php://input'));
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$response = curl_exec($ch);
if (curl_error($ch)){
civiproxy_http_error("CURL error (" . curl_errno($ch) . ") ".curl_error($ch) , 501);
}
// I think that most callbacks just want a response code
http_response_code(curl_getinfo($ch, CURLINFO_HTTP_CODE));
// But some might be interested in the response.
echo $response;
}

69
proxy/callback.php Normal file
View File

@ -0,0 +1,69 @@
<?php
// Handles callback URLs as follows:
// 1. Validates callback
// 2. Passes to civicrm if the payload passes validation
// 2. Returns an appropriate response (an HTML code)
// Note: valid callbacks should be defined as elements of a $callbacks variable
// in config.php.
// Callback URL should be configured in the service as follows:
// "{$proxy_base}/callback.php?source={$callbacks[$key]}&secret={$callbacks[$key]['secret']}"
// where $key is a key defined in the $callbacks variable in config.php.
require_once "config.php";
require_once "proxy.php";
require_once "callback.functions.php";
civiproxy_security_check('callback');
if (!isset($callbacks_enabled) || $callbacks_enabled !==true){
civiproxy_http_error("Feature disabled", 403);
}
// Check that this callback has been defined
parse_str($_SERVER['QUERY_STRING'], $query_params);
if(isset($callbacks[$query_params['source']])){
//Retrieve definition from config
$definition = $callbacks[$query_params['source']];
}else{
civiproxy_http_error("Undefined callback", 403);
}
// Check that a secret has been defined
if(!isset($definition['secret'])){
civiproxy_http_error("No secret defined for this callback", 501);
}
// Check secret has been sent
if(!isset($query_params['secret'])){
civiproxy_http_error("Secret missing from query parameters", 403);
}
// Check secret
if(!isset($query_params['secret']) || $definition['secret'] !== $query_params['secret'] ){
civiproxy_http_error("Invalid secret", 403);
}
if(!in_array($_SERVER['REQUEST_METHOD'], ['POST'])){
civiproxy_http_error("Unsupported request method", 501);
}
// If a request method has been defined, validate it
if(isset($definition['request_method'])){
civiproxy_callback_validate_request_method($definition['request_method'], $_SERVER['REQUEST_METHOD']);
}
// If a content type has been defined, validate it
if(isset($definition['content_type'])){
civiproxy_callback_validate_content_type($definition['content_type'], $_SERVER['CONTENT_TYPE']);
}
// TODO? implement body validation
if(isset($validator['body'])){
civiproxy_callback_validate_body($validator['body'], file_get_contents("php://input"), $_SERVER['CONTENT_TYPE']);
}
// We have passed all the validators, forward the request
// TODO for now, I have written my own method to pass on post requests. Would be
// better to refactor / improve civiproxy_redirect()
civiproxy_callback_redirect($definition['target_path'], $_SERVER['REQUEST_METHOD']);

View File

@ -103,3 +103,12 @@ $rest_allowed_actions = array(
),
);
$callbacks_enabled = false;
$callbacks = [
'sparkpost' => [
// 'secret' => '',
'request_method' => 'POST',
'target_path' => 'civicrm/sparkpost/callback'
]
];