Merge pull request #2730 from Icinga/bugfix/invalid-icinga-2-api-response-2728

Handle invalid Icinga 2 API response types
This commit is contained in:
Eric Lippmann 2017-02-13 14:44:51 +01:00
commit d2341369a9
6 changed files with 130 additions and 36 deletions

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
/**
* Exception thrown by {@link \Icinga\Util\Json::decode()} on failure
*/
class JsonDecodeException extends JsonException
{
}

View File

@ -0,0 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
/**
* Exception thrown by {@link \Icinga\Util\Json::encode()} on failure
*/
class JsonEncodeException extends JsonException
{
}

View File

@ -0,0 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Exception\Json;
use Icinga\Exception\IcingaException;
/**
* Exception thrown by {@link \Icinga\Util\Json} on failure
*/
abstract class JsonException extends IcingaException
{
}

View File

@ -0,0 +1,80 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Util;
use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Exception\Json\JsonEncodeException;
/**
* Wrap {@link json_encode()} and {@link json_decode()} with error handling
*/
class Json
{
/**
* {@link json_encode()} wrapper
*
* @param mixed $value
* @param int $options
* @param int $depth
*
* @return string
* @throws JsonEncodeException
*/
public static function encode($value, $options = 0, $depth = 512)
{
$encoded = json_encode($value, $options, $depth);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonEncodeException('%s: %s', static::lastErrorMsg(), var_export($value, true));
}
return $encoded;
}
/**
* {@link json_decode()} wrapper
*
* @param string $json
* @param bool $assoc
* @param int $depth
* @param int $options
*
* @return mixed
* @throws JsonDecodeException
*/
public static function decode($json, $assoc = false, $depth = 512, $options = 0)
{
$decoded = json_decode($json, $assoc, $depth, $options);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonDecodeException('%s: %s', static::lastErrorMsg(), var_export($json, true));
}
return $decoded;
}
/**
* {@link json_last_error_msg()} replacement for PHP < 5.5.0
*
* @return string
*/
protected static function lastErrorMsg()
{
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
return json_last_error_msg();
}
// All possible error codes before PHP 5.5.0 (except JSON_ERROR_NONE)
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded';
case JSON_ERROR_STATE_MISMATCH:
return 'State mismatch (invalid or malformed JSON)';
case JSON_ERROR_CTRL_CHAR:
return 'Control character error, possibly incorrectly encoded';
case JSON_ERROR_SYNTAX:
return 'Syntax error';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return 'Unknown error';
}
}
}

View File

@ -4,6 +4,7 @@
namespace Icinga\Module\Monitoring\Command\Transport;
use Icinga\Application\Logger;
use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Module\Monitoring\Command\IcingaApiCommand;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer;
@ -193,12 +194,18 @@ class ApiCommandTransport implements CommandTransportInterface
$this->getHost(),
$this->getPort()
);
$response = RestRequest::post($this->getUriFor($command->getEndpoint()))
->authenticateWith($this->getUsername(), $this->getPassword())
->sendJson()
->noStrictSsl()
->setPayload($command->getData())
->send();
try {
$response = RestRequest::post($this->getUriFor($command->getEndpoint()))
->authenticateWith($this->getUsername(), $this->getPassword())
->sendJson()
->noStrictSsl()
->setPayload($command->getData())
->send();
} catch (JsonDecodeException $e) {
throw new CommandTransportException('Got invalid JSON response from the Icinga 2 API: %s', $e->getMessage());
}
if (isset($response['error'])) {
throw new CommandTransportException(
'Can\'t send external Icinga command: %u %s',

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Monitoring\Web\Rest;
use Exception;
use Icinga\Application\Logger;
use Icinga\Util\Json;
/**
* REST Request
@ -261,35 +262,6 @@ class RestRequest
fclose($stream);
}
$response = @json_decode($result, true);
if ($response === null) {
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
throw new Exception(json_last_error_msg());
} else {
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
$msg = 'The maximum stack depth has been exceeded';
break;
case JSON_ERROR_CTRL_CHAR:
$msg = 'Control character error, possibly incorrectly encoded';
break;
case JSON_ERROR_STATE_MISMATCH:
$msg = 'Invalid or malformed JSON';
break;
case JSON_ERROR_SYNTAX:
$msg = 'Syntax error';
break;
case JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$msg = 'An error occured when parsing a JSON string';
}
throw new Exception($msg);
}
}
return $response;
return Json::decode($result, true);
}
}