Compare commits

...

12 Commits

Author SHA1 Message Date
systopia a93c66469d 0.6.dev-303a704 2018-03-21 11:43:49 +01:00
systopia 303a704035 Merge remote-tracking branch 'public/dev_26' into dev_0.6 2018-03-21 11:33:35 +01:00
systopia f5d0dc8311 minor adjustments to #26 2018-03-21 11:31:51 +01:00
systopia 552ad83215 Merge remote-tracking branch 'Michael/config-dist' into dev_0.6 2018-03-21 11:18:22 +01:00
Michael McAndrew 324b8c2e96 Support GET request method 2018-03-12 10:30:01 +00:00
Michael McAndrew abd31ca0ef Allow multiple request methods per callback. 2018-03-12 10:25:31 +00:00
Michael McAndrew 5dedecb2f3 Add checks for supported content types 2018-03-12 09:50:03 +00:00
Michael McAndrew 8f1aba57e7 Merge branch 'master' of https://github.com/systopia/CiviProxy into config-dist 2018-03-12 09:02:29 +00:00
Michael McAndrew 9bf5abf964 Merge branch 'master' of https://github.com/systopia/CiviProxy into callbacks 2018-03-12 09:01:43 +00:00
Michael McAndrew ec3b95aabc first draft callback functionality 2018-03-12 08:56:51 +00:00
Michael McAndrew 1b1d5e8bd4 a hint on installation in the config.dist.php file 2018-03-09 17:25:16 +00:00
Michael McAndrew aaa5330591 distribute a config.dist.php file 2018-03-09 17:24:13 +00:00
7 changed files with 247 additions and 32 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
secrets.php
proxy/file_cache
proxy/config.php
debug.log
.idea

View File

@ -36,16 +36,20 @@ The CiviProxy server is the actual policeman that receives all requests and deci
Once you have installed your CiviProxy server you need to complete a few configuration steps.
### The Config.php file
The configuration of CiviProxy is mainly controlled with one PHP file called `config.php`. You will need to locate that file in your CiviProxy scripts:
The configuration of CiviProxy is mainly controlled with one PHP file called `config.php`. Create this file by copying or renaming the `config.dist.php` file.
![List of files on your CiviProxy server](img/file%20list%20proxy.png)
### Configuring the URL of your CiviProxy server
First thing you need to configure is the base URL of your CiviProxy server using the `$proxy_base` variable in the `config.php` file. As I have used a local test installation I have used `http://localhost/proxy`:
```php
// this should point to the base address of the CiviProxy installation
$proxy_base = 'http://localhost/proxy';
```
### Configuring the link to the secure target CiviCRM
Next thing you want to configure is what your target CiviCRM is. This is the CiviCRM installation which you want CiviProxy to police, so the one where the actual data resides and is collected from or sent to.
The assumption is that this CiviCRM resides in some kind of VPN and will accept traffic only from the CiviProxy IP address (and probably a few trusted others like home workers or support people).

View File

@ -49,6 +49,8 @@ Installing CiviProxy should be pretty straightforward:
* check the [Configuring CiviProxy](configuration.md) page to see how to configure the CiviProxy extension.
## Installing the Proxy scripts your your CiviProxy server
All you need to do is copy the **proxy** folder on the webspace you want to use for CiviProxy.
As described in [Configuring CiviProxy](configuration.md), you will need to make changes to the `config.php` file. You can either do those changes locally first and then move all the files to your webspace, or move all the files to your webspace first and then changes the `config.php` file there.
1. Copy the **proxy** folder on the webspace you want to use for CiviProxy
2. Create a `config.php` file using `config.dist.php` as a template.
See [Configuring CiviProxy](configuration.md) for details on what you need to include in the `config.php` file.

View File

@ -0,0 +1,102 @@
<?php
/*--------------------------------------------------------+
| SYSTOPIA CiviProxy |
| a simple proxy solution for external access to CiviCRM |
| Author: McAndrews (michaelmcandrew@thirdsectordesign.org|
+---------------------------------------------------------*/
function civiproxy_callback_validate_request_method($expected, $actual){
if(is_array($expected) && in_array($actual, $expected)){
return;
}
if(is_string($expected) && $expected == $actual){
return;
}
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
}
// For now, I have written this 'placeholder' method to pass on post requests.
// Sparkpost says that it works OK. Might be a good idea to refactor/improve
// civiproxy_redirect() instead/as well.
function civiproxy_callback_redirect($target_path, $method) {
switch ($method) {
case 'POST':
civiproxy_callback_redirect_post($target_path);
break;
case 'GET':
civiproxy_callback_redirect_get($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;
}
// Change the URL, forward the body. Respond with the response
function civiproxy_callback_redirect_get($target_path) {
global $target_civicrm;
$target_url = "$target_civicrm/{$target_path}";
$ch = curl_init();
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;
}

78
proxy/callback.php Normal file
View File

@ -0,0 +1,78 @@
<?php
/*--------------------------------------------------------+
| SYSTOPIA CiviProxy |
| a simple proxy solution for external access to CiviCRM |
| Author: McAndrews (michaelmcandrew@thirdsectordesign.org|
+---------------------------------------------------------*/
// 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);
}
// Check this is a supported request method
if(!in_array($_SERVER['REQUEST_METHOD'], ['GET', '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']);
}
// Check this is a supported content type
if(!in_array($_SERVER['CONTENT_TYPE'], ['application/json', 'application/x-www-form-urlencoded'])){
civiproxy_http_error("Unsupported content type", 501);
}
// 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
civiproxy_callback_redirect($definition['target_path'], $_SERVER['REQUEST_METHOD']);

View File

@ -8,6 +8,13 @@
+---------------------------------------------------------*/
/****************************************************************
** INSTALLATION **
** **
** 1. Make a copy of this file called config.php **
****************************************************************/
/****************************************************************
** URLS **
****************************************************************/
@ -36,6 +43,11 @@ $target_mail_view = $target_civicrm . '/civicrm/mailing/view';
** GENERAL OPTIONS **
****************************************************************/
// Set this option to enable or disable callback processing
// The individual callbacks still have to be defined
// by the $callbacks variable, see below
$callbacks_enabled = FALSE;
// This logo is shown if the proxy server is address with a web browser
// add your own logo here
$civiproxy_logo = "<img src='{$proxy_base}/static/images/proxy-logo.png' alt='SYSTOPIA Organisationsberatung'></img>";
@ -117,3 +129,19 @@ $rest_allowed_actions = array(
),
),
);
/****************************************************************
** ALLOWED CALLBACKS **
****************************************************************/
// defines the callbacks that are to be forwarded to the target system
// make sure $callbacks_enabled is TRUE for this to work
$callbacks = array(
'sparkpost_example' => array(
'secret' => '85c573b980c3c248f083f9ca6a175659',
'request_method' => 'POST', // single value or array
'content_type' => 'application/json',
'target_path' => 'civicrm/sparkpost/callback'
)
);

View File

@ -8,7 +8,7 @@
+---------------------------------------------------------*/
require_once "config.php";
$civiproxy_version = '0.6.dev1';
$civiproxy_version = '0.6.dev-303a704';
/**
* this will redirect the request to another URL,