Implement a JSON-RPC based client to the Bonnie service API (#3093)
This commit is contained in:
parent
04718ed0ef
commit
4d9522a654
3 changed files with 329 additions and 1 deletions
|
@ -51,4 +51,11 @@ $rcmail_config['kolab_users_id_attrib'] = null;
|
|||
// Use these attributes when searching users in LDAP
|
||||
$rcmail_config['kolab_users_search_attrib'] = array('cn','mail','alias');
|
||||
|
||||
|
||||
// JSON-RPC endpoint configuration of the Bonnie web service providing historic data for groupware objects
|
||||
$rcmail_config['kolab_bonnie_api'] = array(
|
||||
'uri' => 'https://<kolab-hostname>:8080/api/rpc',
|
||||
'user' => 'webclient',
|
||||
'pass' => 'Welcome2KolabSystems',
|
||||
'secret' => '8431f191707fffffff00000000cccc',
|
||||
'debug' => true, // logs requests/responses to <log-dir>/bonnie
|
||||
);
|
||||
|
|
82
plugins/libkolab/lib/kolab_bonnie_api.php
Normal file
82
plugins/libkolab/lib/kolab_bonnie_api.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Provider class for accessing historic groupware object data through the Bonnie service
|
||||
*
|
||||
* API Specification at https://wiki.kolabsys.com/User:Bruederli/Draft:Bonnie_Client_API
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class kolab_bonnie_api
|
||||
{
|
||||
public $ready = false;
|
||||
|
||||
private $config = array();
|
||||
private $client = null;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $confg;
|
||||
|
||||
$this->client = new kolab_bonnie_api_client($config['uri'], $config['timeout'] ?: 5, (bool)$config['debug']);
|
||||
|
||||
$this->client->set_secret($config['secret']);
|
||||
$this->client->set_authentication($config['user'], $config['pass']);
|
||||
$this->client->set_request_user(rcube::get_instance()->get_user_name());
|
||||
|
||||
$this->ready = !empty($config['secret']) && !empty($config['user']) && !empty($config['pass']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for <object>.changelog() API call
|
||||
*/
|
||||
public function changelog($type, $uid, $folder=null)
|
||||
{
|
||||
return $this->client->execute($type.'.changelog', array('uid' => $uid, 'folder' => $folder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for <object>.diff() API call
|
||||
*/
|
||||
public function diff($type, $uid, $rev, $folder=null)
|
||||
{
|
||||
return $this->client->execute($type.'.diff', array('uid' => $uid, 'rev' => $rev, 'folder' => $folder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for <object>.get() API call
|
||||
*/
|
||||
public function get($type, $uid, $rev, $folder=null)
|
||||
{
|
||||
return $this->client->execute($type.'.get', array('uid' => $uid, 'rev' => intval($rev), 'folder' => $folder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic wrapper for direct API calls
|
||||
*/
|
||||
public function _execute($method, $params = array())
|
||||
{
|
||||
return $this->client->execute($method, $params);
|
||||
}
|
||||
|
||||
}
|
239
plugins/libkolab/lib/kolab_bonnie_api_client.php
Normal file
239
plugins/libkolab/lib/kolab_bonnie_api_client.php
Normal file
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* JSON-RPC client class with some extra features for communicating with the Bonnie API service.
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class kolab_bonnie_api_client
|
||||
{
|
||||
/**
|
||||
* URL of the RPC endpoint
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* HTTP client timeout in seconds
|
||||
* @var integer
|
||||
*/
|
||||
protected $timeout;
|
||||
|
||||
/**
|
||||
* Debug flag
|
||||
* @var bool
|
||||
*/
|
||||
protected $debug;
|
||||
|
||||
/**
|
||||
* Username for authentication
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* Password for authentication
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Secret key for request signing
|
||||
* @var string
|
||||
*/
|
||||
protected $secret;
|
||||
|
||||
/**
|
||||
* Default HTTP headers to send to the server
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = array(
|
||||
'Connection' => 'close',
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $url Server URL
|
||||
* @param integer $timeout Request timeout
|
||||
* @param bool $debug Enabled debug logging
|
||||
* @param array $headers Custom HTTP headers
|
||||
*/
|
||||
public function __construct($url, $timeout = 5, $debug = false, $headers = array())
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->timeout = $timeout;
|
||||
$this->debug = $debug;
|
||||
$this->headers = array_merge($this->headers, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for secret key for request signing
|
||||
*/
|
||||
public function set_secret($secret)
|
||||
{
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the X-Request-User header
|
||||
*/
|
||||
public function set_request_user($username)
|
||||
{
|
||||
$this->headers['X-Request-User'] = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set authentication parameters
|
||||
*
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
*/
|
||||
public function set_authentication($username, $password)
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic mapping of procedures
|
||||
*
|
||||
* @param string $method Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $params)
|
||||
{
|
||||
return $this->execute($method, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an RPC command
|
||||
*
|
||||
* @param string $method Procedure name
|
||||
* @param array $params Procedure arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute($method, array $params = array())
|
||||
{
|
||||
$id = mt_rand();
|
||||
|
||||
$payload = array(
|
||||
'jsonrpc' => '2.0',
|
||||
'method' => $method,
|
||||
'id' => $id,
|
||||
);
|
||||
|
||||
if (!empty($params)) {
|
||||
$payload['params'] = $params;
|
||||
}
|
||||
|
||||
$result = $this->send_request($payload, $method != 'system.keygen');
|
||||
|
||||
if (isset($result['id']) && $result['id'] == $id && array_key_exists('result', $result)) {
|
||||
return $result['result'];
|
||||
}
|
||||
else if (isset($result['error'])) {
|
||||
$this->_debug('ERROR', $result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the HTTP request
|
||||
*
|
||||
* @param string $payload Data to send
|
||||
*/
|
||||
protected function send_request($payload, $sign = true)
|
||||
{
|
||||
try {
|
||||
$payload_ = json_encode($payload);
|
||||
|
||||
// add request signature
|
||||
if ($sign && !empty($this->secret)) {
|
||||
$this->headers['X-Request-Sign'] = $this->request_signature($payload_);
|
||||
}
|
||||
else if ($this->headers['X-Request-Sign']) {
|
||||
unset($this->headers['X-Request-Sign']);
|
||||
}
|
||||
|
||||
$this->_debug('REQUEST', $payload, $this->headers);
|
||||
$request = libkolab::http_request($this->url, 'POST', array('timeout' => $this->timeout));
|
||||
$request->setHeader($this->headers);
|
||||
$request->setAuth($this->username, $this->password);
|
||||
$request->setBody($payload_);
|
||||
|
||||
$response = $request->send();
|
||||
|
||||
if ($response->getStatus() == 200) {
|
||||
$result = json_decode($response->getBody(), true);
|
||||
$this->_debug('RESPONSE', $result);
|
||||
}
|
||||
else {
|
||||
throw new Exception(sprintf("HTTP %d %s", $response->getStatus(), $response->getReasonPhrase()));
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 500,
|
||||
'type' => 'php',
|
||||
'message' => "Bonnie API request failed: " . $e->getMessage(),
|
||||
), true);
|
||||
|
||||
return array('id' => $payload['id'], 'error' => $e->getMessage(), 'code' => -32000);
|
||||
}
|
||||
|
||||
return is_array($result) ? $result : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the hmac signature for the current event payload using
|
||||
* the secret key configured for this API client
|
||||
*
|
||||
* @param string $data The request payload data
|
||||
* @return string The request signature
|
||||
*/
|
||||
protected function request_signature($data)
|
||||
{
|
||||
// TODO: get the session key with a system.keygen call
|
||||
return hash_hmac('sha256', $this->headers['X-Request-User'] . ':' . $data, $this->secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write debug log
|
||||
*/
|
||||
protected function _debug(/* $message, $data1, data2, ...*/)
|
||||
{
|
||||
if (!$this->debug)
|
||||
return;
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
$msg = array();
|
||||
foreach ($args as $arg) {
|
||||
$msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
|
||||
}
|
||||
|
||||
rcube::write_log('bonnie', join(";\n", $msg));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue