IcingaObjectHandler: delegate REST API

This commit is contained in:
Thomas Gelf 2017-08-16 08:08:08 +02:00
parent a7b796466f
commit 93cb271554
2 changed files with 207 additions and 24 deletions

View File

@ -0,0 +1,180 @@
<?php
namespace Icinga\Module\Director\RestApi;
use Exception;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Core\CoreApi;
use Icinga\Module\Director\Exception\DuplicateKeyException;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Util;
class IcingaObjectHandler extends RequestHandler
{
/** @var IcingaObject */
protected $object;
/** @var CoreApi */
protected $api;
public function setObject(IcingaObject $object)
{
$this->object = $object;
return $this;
}
public function setApi(CoreApi $api)
{
$this->api = $api;
return $this;
}
/**
* @return IcingaObject
* @throws ProgrammingError
*/
protected function requireObject()
{
if ($this->object === null) {
throw new ProgrammingError('Object is required');
}
return $this->object;
}
/**
* @return IcingaObject
*/
protected function eventuallyLoadObject()
{
return $this->object;
}
protected function requireJsonBody()
{
$data = json_decode($this->request->getRawBody());
if ($data === null) {
$this->response->setHttpResponseCode(400);
throw new IcingaException(
'Invalid JSON: %s',
$this->getLastJsonError()
);
}
return $data;
}
protected function getType()
{
return $this->request->getControllerName();
}
protected function handleApiRequest()
{
try {
$this->processApiRequest();
} catch (NotFoundError $e) {
$this->sendJsonError($e, 404);
return;
} catch (DuplicateKeyException $e) {
$this->sendJsonError($e, 422);
return;
} catch (Exception $e) {
$this->sendJsonError($e);
}
if ($this->request->getActionName() !== 'index') {
throw new NotFoundError('Not found');
}
}
protected function processApiRequest()
{
$request = $this->request;
$response = $this->response;
$db = $this->db;
// TODO: I hate doing this:
if ($this->request->getActionName() === 'ticket') {
$host = $this->requireObject();
if ($host->getResolvedProperty('has_agent') !== 'y') {
throw new NotFoundError('The host "%s" is not an agent', $host->getObjectName());
}
$this->sendJson(
Util::getIcingaTicket(
$host->getObjectName(),
$this->api->getTicketSalt()
)
);
return;
}
switch ($request->getMethod()) {
case 'DELETE':
$object = $this->requireObject();
$object->delete();
$this->sendJson($object->toPlainObject(false, true));
break;
case 'POST':
case 'PUT':
$data = (array) $this->requireJsonBody();
$type = $this->getType();
if ($object = $this->eventuallyLoadObject()) {
if ($request->getMethod() === 'POST') {
$object->setProperties($data);
} else {
$data = array_merge([
'object_type' => $object->get('object_type'),
'object_name' => $object->getObjectName()
], $data);
$object->replaceWith(
IcingaObject::createByType($type, $data, $db)
);
}
} else {
$object = IcingaObject::createByType($type, $data, $db);
}
if ($object->hasBeenModified()) {
$status = $object->hasBeenLoadedFromDb() ? 200 : 201;
$object->store();
$response->setHttpResponseCode($status);
} else {
$response->setHttpResponseCode(304);
}
$this->sendJson($object->toPlainObject(false, true));
break;
case 'GET':
$params = $this->request->getUrl()->getParams();
$this->requireObject();
$properties = $params->shift('properties');
if (strlen($properties)) {
$properties = preg_split('/\s*,\s*/', $properties, -1, PREG_SPLIT_NO_EMPTY);
} else {
$properties = null;
}
$this->sendJson(
$this->requireObject()->toPlainObject(
$params->shift('resolved'),
! $params->shift('withNull'),
$properties
)
);
break;
default:
$request->getResponse()->setHttpResponseCode(400);
throw new IcingaException('Unsupported method ' . $request->getMethod());
}
}
}

View File

@ -29,8 +29,10 @@ abstract class ObjectController extends ActionController
/** @var IcingaObject */
protected $object;
/** @var bool This controller handles REST API requests */
protected $isApified = true;
/** @var array Allowed object types we are allowed to edit anyways */
protected $allowedExternals = array(
'apiuser',
'endpoint'
@ -40,30 +42,24 @@ abstract class ObjectController extends ActionController
{
parent::init();
$this->eventuallyLoadObject();
if ($this->getRequest()->isApiRequest()) {
$this->handleApiRequest();
} else {
$type = strtolower($this->getType());
if (null !== $this->params->get('name') || $this->params->get('id')) {
$this->loadObject();
$handler = new IcingaObjectHandler($this->getRequest(), $this->getResponse(), $this->db());
$handler->setApi($this->api());
if ($this->object) {
$handler->setObject($this->object);
}
$this->tabs(new ObjectTabs($type, $this->getAuth(), $this->object));
$handler->dispatch();
} else {
$this->tabs(new ObjectTabs($this->getType(), $this->getAuth(), $this->object));
}
}
public function indexAction()
{
if ($this->getRequest()->isApiRequest()) {
return;
}
if ($this->object
&& $this->object->isExternal()
&& ! in_array($this->object->getShortTableName(), $this->allowedExternals)
) {
$this->redirectNow(
$this->getRequest()->getUrl()->setPath(sprintf('director/%s/render', $this->getType()))
);
if (! $this->getRequest()->isApiRequest()) {
$this->redirectToPreviewForExternals()
->editAction();
}
$this->editAction();
@ -266,10 +262,23 @@ abstract class ObjectController extends ActionController
);
}
protected function eventuallyLoadObject()
{
if (null !== $this->params->get('name') || $this->params->get('id')) {
$this->loadObject();
}
}
protected function loadObject()
{
if ($this->object === null) {
if (null !== ($name = $this->params->get('name'))) {
if ($id = $this->params->get('id')) {
$this->object = IcingaObject::loadByType(
$this->getType(),
(int) $id,
$this->db()
);
} elseif (null !== ($name = $this->params->get('name'))) {
$this->object = IcingaObject::loadByType(
$this->getType(),
$name,
@ -280,12 +289,6 @@ abstract class ObjectController extends ActionController
$this->object = null;
throw new NotFoundError('No such object available');
}
} elseif ($id = $this->params->get('id')) {
$this->object = IcingaObject::loadByType(
$this->getType(),
(int) $id,
$this->db()
);
} elseif ($this->getRequest()->isApiRequest()) {
if ($this->getRequest()->isGet()) {
$this->getResponse()->setHttpResponseCode(422);