diff --git a/library/Director/RestApi/IcingaObjectHandler.php b/library/Director/RestApi/IcingaObjectHandler.php new file mode 100644 index 00000000..703cfd7d --- /dev/null +++ b/library/Director/RestApi/IcingaObjectHandler.php @@ -0,0 +1,180 @@ +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()); + } + } +} diff --git a/library/Director/Web/Controller/ObjectController.php b/library/Director/Web/Controller/ObjectController.php index a409d61a..7c557e48 100644 --- a/library/Director/Web/Controller/ObjectController.php +++ b/library/Director/Web/Controller/ObjectController.php @@ -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);