/** * Checks that api access config check works */ public function testDataObjectAPIEnaled() { Config::inst()->update('RESTfulAPI', 'access_control_policy', 'ACL_CHECK_CONFIG_ONLY'); // ---------------- // Method Calls // Disabled by default $enabled = RESTfulAPI::api_access_control('ApiTest_Author'); $this->assertFalse($enabled, 'Access control should return FALSE by default'); // Enabled Config::inst()->update('ApiTest_Author', 'api_access', true); $enabled = RESTfulAPI::api_access_control('ApiTest_Author'); $this->assertTrue($enabled, 'Access control should return TRUE when api_access is enbaled'); // Method specific Config::inst()->update('ApiTest_Author', 'api_access', 'GET,POST'); $enabled = RESTfulAPI::api_access_control('ApiTest_Author'); $this->assertTrue($enabled, 'Access control should return TRUE when api_access is enbaled with default GET method'); $enabled = RESTfulAPI::api_access_control('ApiTest_Author', 'POST'); $this->assertTrue($enabled, 'Access control should return TRUE when api_access match HTTP method'); $enabled = RESTfulAPI::api_access_control('ApiTest_Author', 'PUT'); $this->assertFalse($enabled, 'Access control should return FALSE when api_access does not match method'); // ---------------- // API Calls /* // Access authorised $response = Director::test('api/ApiTest_Author/1', null, null, 'GET'); $this->assertEquals( $response->getStatusCode(), 200 ); // Access denied Config::inst()->update('ApiTest_Author', 'api_access', false); $response = Director::test('api/ApiTest_Author/1', null, null, 'GET'); $this->assertEquals( $response->getStatusCode(), 403 ); // Access denied Config::inst()->update('ApiTest_Author', 'api_access', 'POST'); $response = Director::test('api/ApiTest_Author/1', null, null, 'GET'); $this->assertEquals( $response->getStatusCode(), 403 ); */ }
/** * Delete object of Class $model and ID $id * * @todo Respond with a 204 status message on success? * * @param string $model Model class * @param integer $id Model ID * @param SS_HTTPRequest $request Model ID * @return NULL|array NULL if successful or array with error detail */ function deleteModel($model, $id, SS_HTTPRequest $request) { if ($id) { $object = DataObject::get_by_id($model, $id); if ($object) { if (!RESTfulAPI::api_access_control($object, $request->httpMethod())) { return new RESTfulAPI_Error(403, "API access denied."); } $object->delete(); } else { return new RESTfulAPI_Error(404, "Record not found."); } } else { //shouldn't happen but just in case return new RESTfulAPI_Error(400, "Invalid or missing ID. Received '{$id}'."); } return NULL; }
/** * Format a DataObject keys and values * ready to be turned into JSON * * @param DataObject $dataObject The data object to format * @return array|null The formatted array map representation of the DataObject or null is permission denied */ protected function formatDataObject(DataObject $dataObject) { // api access control if (!RESTfulAPI::api_access_control($dataObject, 'GET')) { return null; } if (method_exists($dataObject, 'onBeforeSerialize')) { $dataObject->onBeforeSerialize(); } $dataObject->extend('onBeforeSerialize'); // setup $formattedDataObjectMap = array(); // get DataObject config $db = Config::inst()->get($dataObject->ClassName, 'db'); $has_one = Config::inst()->get($dataObject->ClassName, 'has_one'); $has_many = Config::inst()->get($dataObject->ClassName, 'has_many'); $many_many = Config::inst()->get($dataObject->ClassName, 'many_many'); $belongs_many_many = Config::inst()->get($dataObject->ClassName, 'belongs_many_many'); //$many_many_extraFields = $dataObject->many_many_extraFields(); $many_many_extraFields = $dataObject->stat('many_many_extraFields'); // setup ID (not included in $db!!) $serializedColumnName = $this->serializeColumnName('ID'); $formattedDataObjectMap[$serializedColumnName] = $dataObject->getField('ID'); // iterate over simple DB fields if (!$db) { $db = array(); } foreach ($db as $columnName => $fieldClassName) { $serializedColumnName = $this->serializeColumnName($columnName); $formattedDataObjectMap[$serializedColumnName] = $dataObject->getField($columnName); } // iterate over has_one relations if (!$has_one) { $has_one = array(); } foreach ($has_one as $columnName => $fieldClassName) { $serializedColumnName = $this->serializeColumnName($columnName); // convert foreign ID to integer $relationID = intVal($dataObject->{$columnName . 'ID'}); // skip empty relations if ($relationID === 0) { continue; } // check if this should be embedded if ($this->isEmbeddable($dataObject->ClassName, $columnName)) { // get the relation's record ready to embed $embedData = $this->getEmbedData($dataObject, $columnName); // embed the data if any if ($embedData !== null) { $formattedDataObjectMap[$serializedColumnName] = $embedData; } } else { // save foreign ID $formattedDataObjectMap[$serializedColumnName] = $relationID; } } // combine defined '_many' relations into 1 array $many_relations = array(); if (is_array($has_many)) { $many_relations = array_merge($many_relations, $has_many); } if (is_array($many_many)) { $many_relations = array_merge($many_relations, $many_many); } if (is_array($belongs_many_many)) { $many_relations = array_merge($many_relations, $belongs_many_many); } // iterate '_many' relations foreach ($many_relations as $relationName => $relationClassname) { //get the DataList for this realtion's name $dataList = $dataObject->{$relationName}(); //if there actually are objects in the relation if ($dataList->count()) { // check if this relation should be embedded if ($this->isEmbeddable($dataObject->ClassName, $relationName)) { // get the relation's record(s) ready to embed $embedData = $this->getEmbedData($dataObject, $relationName); // embed the data if any if ($embedData !== null) { $serializedColumnName = $this->serializeColumnName($relationName); $formattedDataObjectMap[$serializedColumnName] = $embedData; } } else { // set column value to ID list $idList = $dataList->map('ID', 'ID')->keys(); $serializedColumnName = $this->serializeColumnName($relationName); $formattedDataObjectMap[$serializedColumnName] = $idList; } } } if ($many_many_extraFields) { $extraFieldsData = array(); // loop through extra fields config foreach ($many_many_extraFields as $relation => $fields) { $manyManyDataObjects = $dataObject->{$relation}(); $relationData = array(); // get the extra data for each object in the relation foreach ($manyManyDataObjects as $manyManyDataObject) { $data = $manyManyDataObjects->getExtraData($relation, $manyManyDataObject->ID); // format data foreach ($data as $key => $value) { // clear empty data if (!$value) { unset($data[$key]); continue; } $newKey = $this->serializeColumnName($key); if ($newKey != $key) { unset($data[$key]); $data[$newKey] = $value; } } // store if there is any real data if ($data) { $relationData[$manyManyDataObject->ID] = $data; } } // add individual DO extra data to the relation's extra data if ($relationData) { $key = $this->serializeColumnName($relation); $extraFieldsData[$key] = $relationData; } } // save the extrafields data if ($extraFieldsData) { $key = $this->serializeColumnName('ManyManyExtraFields'); $formattedDataObjectMap[$key] = $extraFieldsData; } } if (method_exists($dataObject, 'onAfterSerialize')) { $dataObject->onAfterSerialize($formattedDataObjectMap); } $dataObject->extend('onAfterSerialize', $formattedDataObjectMap); return $formattedDataObjectMap; }