1<?php 2 3namespace Icinga\Module\Director\RestApi; 4 5use Exception; 6use Icinga\Exception\IcingaException; 7use Icinga\Exception\NotFoundError; 8use Icinga\Exception\ProgrammingError; 9use Icinga\Module\Director\Core\CoreApi; 10use Icinga\Module\Director\Exception\DuplicateKeyException; 11use Icinga\Module\Director\Objects\IcingaObject; 12use Icinga\Module\Director\Util; 13 14class IcingaObjectHandler extends RequestHandler 15{ 16 /** @var IcingaObject */ 17 protected $object; 18 19 /** @var CoreApi */ 20 protected $api; 21 22 public function setObject(IcingaObject $object) 23 { 24 $this->object = $object; 25 return $this; 26 } 27 28 public function setApi(CoreApi $api) 29 { 30 $this->api = $api; 31 return $this; 32 } 33 34 /** 35 * @return IcingaObject 36 * @throws ProgrammingError 37 */ 38 protected function requireObject() 39 { 40 if ($this->object === null) { 41 throw new ProgrammingError('Object is required'); 42 } 43 44 return $this->object; 45 } 46 47 /** 48 * @return IcingaObject 49 */ 50 protected function eventuallyLoadObject() 51 { 52 return $this->object; 53 } 54 55 protected function requireJsonBody() 56 { 57 $data = json_decode($this->request->getRawBody()); 58 59 if ($data === null) { 60 $this->response->setHttpResponseCode(400); 61 throw new IcingaException( 62 'Invalid JSON: %s', 63 $this->getLastJsonError() 64 ); 65 } 66 67 return $data; 68 } 69 70 protected function getType() 71 { 72 return $this->request->getControllerName(); 73 } 74 75 protected function processApiRequest() 76 { 77 try { 78 $this->handleApiRequest(); 79 } catch (NotFoundError $e) { 80 $this->sendJsonError($e, 404); 81 return; 82 } catch (DuplicateKeyException $e) { 83 $this->sendJsonError($e, 422); 84 return; 85 } catch (Exception $e) { 86 $this->sendJsonError($e); 87 } 88 89 if ($this->request->getActionName() !== 'index') { 90 throw new NotFoundError('Not found'); 91 } 92 } 93 94 protected function handleApiRequest() 95 { 96 $request = $this->request; 97 $response = $this->response; 98 $db = $this->db; 99 100 // TODO: I hate doing this: 101 if ($this->request->getActionName() === 'ticket') { 102 $host = $this->requireObject(); 103 104 if ($host->getResolvedProperty('has_agent') !== 'y') { 105 throw new NotFoundError('The host "%s" is not an agent', $host->getObjectName()); 106 } 107 108 $this->sendJson( 109 Util::getIcingaTicket( 110 $host->getObjectName(), 111 $this->api->getTicketSalt() 112 ) 113 ); 114 115 // TODO: find a better way to shut down. Currently, this avoids 116 // "not found" errors: 117 exit; 118 } 119 120 switch ($request->getMethod()) { 121 case 'DELETE': 122 $object = $this->requireObject(); 123 $object->delete(); 124 $this->sendJson($object->toPlainObject(false, true)); 125 break; 126 127 case 'POST': 128 case 'PUT': 129 $data = (array) $this->requireJsonBody(); 130 $type = $this->getType(); 131 if ($object = $this->eventuallyLoadObject()) { 132 if ($request->getMethod() === 'POST') { 133 $object->setProperties($data); 134 } else { 135 $data = array_merge([ 136 'object_type' => $object->get('object_type'), 137 'object_name' => $object->getObjectName() 138 ], $data); 139 $object->replaceWith( 140 IcingaObject::createByType($type, $data, $db) 141 ); 142 } 143 } else { 144 $object = IcingaObject::createByType($type, $data, $db); 145 } 146 147 if ($object->hasBeenModified()) { 148 $status = $object->hasBeenLoadedFromDb() ? 200 : 201; 149 $object->store(); 150 $response->setHttpResponseCode($status); 151 } else { 152 $response->setHttpResponseCode(304); 153 } 154 155 $this->sendJson($object->toPlainObject(false, true)); 156 break; 157 158 case 'GET': 159 $params = $this->request->getUrl()->getParams(); 160 $this->requireObject(); 161 $properties = $params->shift('properties'); 162 if (strlen($properties)) { 163 $properties = preg_split('/\s*,\s*/', $properties, -1, PREG_SPLIT_NO_EMPTY); 164 } else { 165 $properties = null; 166 } 167 168 $this->sendJson( 169 $this->requireObject()->toPlainObject( 170 $params->shift('resolved'), 171 ! $params->shift('withNull'), 172 $properties 173 ) 174 ); 175 break; 176 177 default: 178 $request->getResponse()->setHttpResponseCode(400); 179 throw new IcingaException('Unsupported method ' . $request->getMethod()); 180 } 181 } 182} 183