[CVE-2019-12149] Fixed potential SQL injection vulnerability in RestfulServer

This commit is contained in:
Robbie Averill 2019-05-20 15:42:44 +12:00
parent a737f67a13
commit 65239cd54d
3 changed files with 138 additions and 95 deletions

View File

@ -177,16 +177,22 @@ class RestfulServer extends Controller
*/ */
protected function getHandler($className, $id, $relationName) protected function getHandler($className, $id, $relationName)
{ {
$sort = ''; $sort = array('ID' => 'ASC');
if ($this->request->getVar('sort')) { if ($sortQuery = $this->request->getVar('sort')) {
$dir = $this->request->getVar('dir'); /** @var DataObject $singleton */
$sort = array($this->request->getVar('sort') => ($dir ? $dir : 'ASC')); $singleton = singleton($className);
// Only apply a sort filter if it is a valid field on the DataObject
if ($singleton && $singleton->hasDatabaseField($sortQuery)) {
$sort = array(
$sortQuery => $this->request->getVar('dir') === 'DESC' ? 'DESC' : 'ASC',
);
}
} }
$limit = array( $limit = array(
'start' => $this->request->getVar('start'), 'start' => (int) $this->request->getVar('start'),
'limit' => $this->request->getVar('limit') 'limit' => (int) $this->request->getVar('limit'),
); );
$params = $this->request->getVars(); $params = $this->request->getVars();

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* *
* @todo Test Relation getters * @todo Test Relation getters
* @todo Test filter and limit through GET params * @todo Test filter and limit through GET params
* @todo Test DELETE verb * @todo Test DELETE verb
@ -22,28 +22,28 @@ class RestfulServerTest extends SapphireTest
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$page1 = $this->objFromFixture('RestfulServerTest_Page', 'page1'); $page1 = $this->objFromFixture('RestfulServerTest_Page', 'page1');
// normal GET should succeed with $api_access enabled // normal GET should succeed with $api_access enabled
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 200); $this->assertEquals($response->getStatusCode(), 200);
$_SERVER['PHP_AUTH_USER'] = 'user@test.com'; $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
$_SERVER['PHP_AUTH_PW'] = 'user'; $_SERVER['PHP_AUTH_PW'] = 'user';
// even with logged in user a GET with $api_access disabled should fail // even with logged in user a GET with $api_access disabled should fail
$url = "/api/v1/RestfulServerTest_Page/" . $page1->ID; $url = "/api/v1/RestfulServerTest_Page/" . $page1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 401); $this->assertEquals($response->getStatusCode(), 401);
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testApiAccessBoolean() public function testApiAccessBoolean()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertContains('<ID>', $response->getBody()); $this->assertContains('<ID>', $response->getBody());
@ -52,7 +52,7 @@ class RestfulServerTest extends SapphireTest
$this->assertContains('<Page', $response->getBody()); $this->assertContains('<Page', $response->getBody());
$this->assertContains('<Author', $response->getBody()); $this->assertContains('<Author', $response->getBody());
} }
public function testAuthenticatedGET() public function testAuthenticatedGET()
{ {
$thing1 = $this->objFromFixture('RestfulServerTest_SecretThing', 'thing1'); $thing1 = $this->objFromFixture('RestfulServerTest_SecretThing', 'thing1');
@ -62,25 +62,25 @@ class RestfulServerTest extends SapphireTest
$url = "/api/v1/RestfulServerTest_SecretThing/" . $thing1->ID; $url = "/api/v1/RestfulServerTest_SecretThing/" . $thing1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 401); $this->assertEquals($response->getStatusCode(), 401);
$_SERVER['PHP_AUTH_USER'] = 'user@test.com'; $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
$_SERVER['PHP_AUTH_PW'] = 'user'; $_SERVER['PHP_AUTH_PW'] = 'user';
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 200); $this->assertEquals($response->getStatusCode(), 200);
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testAuthenticatedPUT() public function testAuthenticatedPUT()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$data = array('Comment' => 'created'); $data = array('Comment' => 'created');
$response = Director::test($url, $data, null, 'PUT'); $response = Director::test($url, $data, null, 'PUT');
$this->assertEquals($response->getStatusCode(), 401); // Permission failure $this->assertEquals($response->getStatusCode(), 401); // Permission failure
@ -92,21 +92,21 @@ class RestfulServerTest extends SapphireTest
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testGETRelationshipsXML() public function testGETRelationshipsXML()
{ {
$author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1'); $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
$rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1'); $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
$rating2 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating2'); $rating2 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating2');
// @todo should be set up by fixtures, doesn't work for some reason... // @todo should be set up by fixtures, doesn't work for some reason...
$author1->Ratings()->add($rating1); $author1->Ratings()->add($rating1);
$author1->Ratings()->add($rating2); $author1->Ratings()->add($rating2);
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 200); $this->assertEquals($response->getStatusCode(), 200);
$responseArr = Convert::xml2array($response->getBody()); $responseArr = Convert::xml2array($response->getBody());
$ratingsArr = $responseArr['Ratings']['RestfulServerTest_AuthorRating']; $ratingsArr = $responseArr['Ratings']['RestfulServerTest_AuthorRating'];
$this->assertEquals(count($ratingsArr), 2); $this->assertEquals(count($ratingsArr), 2);
@ -117,20 +117,20 @@ class RestfulServerTest extends SapphireTest
$this->assertContains($rating1->ID, $ratingIDs); $this->assertContains($rating1->ID, $ratingIDs);
$this->assertContains($rating2->ID, $ratingIDs); $this->assertContains($rating2->ID, $ratingIDs);
} }
public function testGETManyManyRelationshipsXML() public function testGETManyManyRelationshipsXML()
{ {
// author4 has related authors author2 and author3 // author4 has related authors author2 and author3
$author2 = $this->objFromFixture('RestfulServerTest_Author', 'author2'); $author2 = $this->objFromFixture('RestfulServerTest_Author', 'author2');
$author3 = $this->objFromFixture('RestfulServerTest_Author', 'author3'); $author3 = $this->objFromFixture('RestfulServerTest_Author', 'author3');
$author4 = $this->objFromFixture('RestfulServerTest_Author', 'author4'); $author4 = $this->objFromFixture('RestfulServerTest_Author', 'author4');
$url = "/api/v1/RestfulServerTest_Author/" . $author4->ID . '/RelatedAuthors'; $url = "/api/v1/RestfulServerTest_Author/" . $author4->ID . '/RelatedAuthors';
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$arr = Convert::xml2array($response->getBody()); $arr = Convert::xml2array($response->getBody());
$authorsArr = $arr['RestfulServerTest_Author']; $authorsArr = $arr['RestfulServerTest_Author'];
$this->assertEquals(count($authorsArr), 2); $this->assertEquals(count($authorsArr), 2);
$ratingIDs = array( $ratingIDs = array(
(int)$authorsArr[0]['ID'], (int)$authorsArr[0]['ID'],
@ -143,10 +143,10 @@ class RestfulServerTest extends SapphireTest
public function testPUTWithFormEncoded() public function testPUTWithFormEncoded()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$_SERVER['PHP_AUTH_USER'] = 'editor@test.com'; $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
$_SERVER['PHP_AUTH_PW'] = 'editor'; $_SERVER['PHP_AUTH_PW'] = 'editor';
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$body = 'Name=Updated Comment&Comment=updated'; $body = 'Name=Updated Comment&Comment=updated';
$headers = array( $headers = array(
@ -159,18 +159,18 @@ class RestfulServerTest extends SapphireTest
$this->assertEquals($responseArr['ID'], $comment1->ID); $this->assertEquals($responseArr['ID'], $comment1->ID);
$this->assertEquals($responseArr['Comment'], 'updated'); $this->assertEquals($responseArr['Comment'], 'updated');
$this->assertEquals($responseArr['Name'], 'Updated Comment'); $this->assertEquals($responseArr['Name'], 'Updated Comment');
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testPOSTWithFormEncoded() public function testPOSTWithFormEncoded()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$_SERVER['PHP_AUTH_USER'] = 'editor@test.com'; $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
$_SERVER['PHP_AUTH_PW'] = 'editor'; $_SERVER['PHP_AUTH_PW'] = 'editor';
$url = "/api/v1/RestfulServerTest_Comment"; $url = "/api/v1/RestfulServerTest_Comment";
$body = 'Name=New Comment&Comment=created'; $body = 'Name=New Comment&Comment=created';
$headers = array( $headers = array(
@ -221,7 +221,7 @@ class RestfulServerTest extends SapphireTest
$obj = Convert::json2obj($response->getBody()); $obj = Convert::json2obj($response->getBody());
$this->assertEquals($obj->ID, $comment1->ID); $this->assertEquals($obj->ID, $comment1->ID);
$this->assertEquals($obj->Comment, 'updated'); $this->assertEquals($obj->Comment, 'updated');
// by extension // by extension
$url = sprintf("/api/v1/RestfulServerTest_Comment/%d.json", $comment1->ID); $url = sprintf("/api/v1/RestfulServerTest_Comment/%d.json", $comment1->ID);
$body = '{"Comment":"updated"}'; $body = '{"Comment":"updated"}';
@ -234,18 +234,18 @@ class RestfulServerTest extends SapphireTest
$obj = Convert::json2obj($response->getBody()); $obj = Convert::json2obj($response->getBody());
$this->assertEquals($obj->ID, $comment1->ID); $this->assertEquals($obj->ID, $comment1->ID);
$this->assertEquals($obj->Comment, 'updated'); $this->assertEquals($obj->Comment, 'updated');
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testPUTwithXML() public function testPUTwithXML()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$_SERVER['PHP_AUTH_USER'] = 'editor@test.com'; $_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
$_SERVER['PHP_AUTH_PW'] = 'editor'; $_SERVER['PHP_AUTH_PW'] = 'editor';
// by mimetype // by mimetype
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>'; $body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>';
@ -254,7 +254,7 @@ class RestfulServerTest extends SapphireTest
$obj = Convert::xml2array($response->getBody()); $obj = Convert::xml2array($response->getBody());
$this->assertEquals($obj['ID'], $comment1->ID); $this->assertEquals($obj['ID'], $comment1->ID);
$this->assertEquals($obj['Comment'], 'updated'); $this->assertEquals($obj['Comment'], 'updated');
// by extension // by extension
$url = sprintf("/api/v1/RestfulServerTest_Comment/%d.xml", $comment1->ID); $url = sprintf("/api/v1/RestfulServerTest_Comment/%d.xml", $comment1->ID);
$body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>'; $body = '<RestfulServerTest_Comment><Comment>updated</Comment></RestfulServerTest_Comment>';
@ -267,17 +267,17 @@ class RestfulServerTest extends SapphireTest
$obj = Convert::xml2array($response->getBody()); $obj = Convert::xml2array($response->getBody());
$this->assertEquals($obj['ID'], $comment1->ID); $this->assertEquals($obj['ID'], $comment1->ID);
$this->assertEquals($obj['Comment'], 'updated'); $this->assertEquals($obj['Comment'], 'updated');
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testHTTPAcceptAndContentType() public function testHTTPAcceptAndContentType()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$headers = array('Accept' => 'application/json'); $headers = array('Accept' => 'application/json');
$response = Director::test($url, null, null, 'GET', null, $headers); $response = Director::test($url, null, null, 'GET', null, $headers);
$this->assertEquals($response->getStatusCode(), 200); // Success $this->assertEquals($response->getStatusCode(), 200); // Success
@ -285,68 +285,68 @@ class RestfulServerTest extends SapphireTest
$this->assertEquals($obj->ID, $comment1->ID); $this->assertEquals($obj->ID, $comment1->ID);
$this->assertEquals($response->getHeader('Content-Type'), 'application/json'); $this->assertEquals($response->getHeader('Content-Type'), 'application/json');
} }
public function testNotFound() public function testNotFound()
{ {
$_SERVER['PHP_AUTH_USER'] = 'user@test.com'; $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
$_SERVER['PHP_AUTH_PW'] = 'user'; $_SERVER['PHP_AUTH_PW'] = 'user';
$url = "/api/v1/RestfulServerTest_Comment/99"; $url = "/api/v1/RestfulServerTest_Comment/99";
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals($response->getStatusCode(), 404); $this->assertEquals($response->getStatusCode(), 404);
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testMethodNotAllowed() public function testMethodNotAllowed()
{ {
$comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1'); $comment1 = $this->objFromFixture('RestfulServerTest_Comment', 'comment1');
$url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID; $url = "/api/v1/RestfulServerTest_Comment/" . $comment1->ID;
$response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD'); $response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD');
$this->assertEquals($response->getStatusCode(), 405); $this->assertEquals($response->getStatusCode(), 405);
} }
public function testConflictOnExistingResourceWhenUsingPost() public function testConflictOnExistingResourceWhenUsingPost()
{ {
$rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1'); $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
$response = Director::test($url, null, null, 'POST'); $response = Director::test($url, null, null, 'POST');
$this->assertEquals($response->getStatusCode(), 409); $this->assertEquals($response->getStatusCode(), 409);
} }
public function testUnsupportedMediaType() public function testUnsupportedMediaType()
{ {
$_SERVER['PHP_AUTH_USER'] = 'user@test.com'; $_SERVER['PHP_AUTH_USER'] = 'user@test.com';
$_SERVER['PHP_AUTH_PW'] = 'user'; $_SERVER['PHP_AUTH_PW'] = 'user';
$url = "/api/v1/RestfulServerTest_Comment"; $url = "/api/v1/RestfulServerTest_Comment";
$data = "Comment||\/||updated"; // weird format $data = "Comment||\/||updated"; // weird format
$headers = array('Content-Type' => 'text/weirdformat'); $headers = array('Content-Type' => 'text/weirdformat');
$response = Director::test($url, null, null, 'POST', $data, $headers); $response = Director::test($url, null, null, 'POST', $data, $headers);
$this->assertEquals($response->getStatusCode(), 415); $this->assertEquals($response->getStatusCode(), 415);
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
public function testXMLValueFormatting() public function testXMLValueFormatting()
{ {
$rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1'); $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertContains('<ID>' . $rating1->ID . '</ID>', $response->getBody()); $this->assertContains('<ID>' . $rating1->ID . '</ID>', $response->getBody());
$this->assertContains('<Rating>' . $rating1->Rating . '</Rating>', $response->getBody()); $this->assertContains('<Rating>' . $rating1->Rating . '</Rating>', $response->getBody());
} }
public function testApiAccessFieldRestrictions() public function testApiAccessFieldRestrictions()
{ {
$author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1'); $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
$rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1'); $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertContains('<ID>', $response->getBody()); $this->assertContains('<ID>', $response->getBody());
@ -354,7 +354,7 @@ class RestfulServerTest extends SapphireTest
$this->assertContains('<Author', $response->getBody()); $this->assertContains('<Author', $response->getBody());
$this->assertNotContains('<SecretField>', $response->getBody()); $this->assertNotContains('<SecretField>', $response->getBody());
$this->assertNotContains('<SecretRelation>', $response->getBody()); $this->assertNotContains('<SecretRelation>', $response->getBody());
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?add_fields=SecretField,SecretRelation'; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?add_fields=SecretField,SecretRelation';
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertNotContains('<SecretField>', $response->getBody(), $this->assertNotContains('<SecretField>', $response->getBody(),
@ -363,7 +363,7 @@ class RestfulServerTest extends SapphireTest
$this->assertNotContains('<SecretRelation>', $response->getBody(), $this->assertNotContains('<SecretRelation>', $response->getBody(),
'"add_fields" URL parameter filters out disallowed relations from $api_access' '"add_fields" URL parameter filters out disallowed relations from $api_access'
); );
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?fields=SecretField,SecretRelation'; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID . '?fields=SecretField,SecretRelation';
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertNotContains('<SecretField>', $response->getBody(), $this->assertNotContains('<SecretField>', $response->getBody(),
@ -372,7 +372,7 @@ class RestfulServerTest extends SapphireTest
$this->assertNotContains('<SecretRelation>', $response->getBody(), $this->assertNotContains('<SecretRelation>', $response->getBody(),
'"fields" URL parameter filters out disallowed relations from $api_access' '"fields" URL parameter filters out disallowed relations from $api_access'
); );
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . '/Ratings'; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . '/Ratings';
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertContains('<Rating>', $response->getBody(), $this->assertContains('<Rating>', $response->getBody(),
@ -382,38 +382,38 @@ class RestfulServerTest extends SapphireTest
'Relation viewer on has-many filters out disallowed fields from $api_access' 'Relation viewer on has-many filters out disallowed fields from $api_access'
); );
} }
public function testApiAccessRelationRestrictionsInline() public function testApiAccessRelationRestrictionsInline()
{ {
$author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1'); $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID;
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertNotContains('<RelatedPages', $response->getBody(), 'Restricts many-many with api_access=false'); $this->assertNotContains('<RelatedPages', $response->getBody(), 'Restricts many-many with api_access=false');
$this->assertNotContains('<PublishedPages', $response->getBody(), 'Restricts has-many with api_access=false'); $this->assertNotContains('<PublishedPages', $response->getBody(), 'Restricts has-many with api_access=false');
} }
public function testApiAccessRelationRestrictionsOnEndpoint() public function testApiAccessRelationRestrictionsOnEndpoint()
{ {
$author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1'); $author1 = $this->objFromFixture('RestfulServerTest_Author', 'author1');
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/ProfilePage"; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/ProfilePage";
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals(404, $response->getStatusCode(), 'Restricts has-one with api_access=false'); $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-one with api_access=false');
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/RelatedPages"; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/RelatedPages";
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals(404, $response->getStatusCode(), 'Restricts many-many with api_access=false'); $this->assertEquals(404, $response->getStatusCode(), 'Restricts many-many with api_access=false');
$url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/PublishedPages"; $url = "/api/v1/RestfulServerTest_Author/" . $author1->ID . "/PublishedPages";
$response = Director::test($url, null, null, 'GET'); $response = Director::test($url, null, null, 'GET');
$this->assertEquals(404, $response->getStatusCode(), 'Restricts has-many with api_access=false'); $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-many with api_access=false');
} }
public function testApiAccessWithPUT() public function testApiAccessWithPUT()
{ {
$rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1'); $rating1 = $this->objFromFixture('RestfulServerTest_AuthorRating', 'rating1');
$url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID; $url = "/api/v1/RestfulServerTest_AuthorRating/" . $rating1->ID;
$data = array( $data = array(
'Rating' => '42', 'Rating' => '42',
@ -448,7 +448,7 @@ class RestfulServerTest extends SapphireTest
'{"FirstName":"User","Email":"user@test.com"}]}', '{"FirstName":"User","Email":"user@test.com"}]}',
"Correct JSON formatting on a dataobjectset with field filter"); "Correct JSON formatting on a dataobjectset with field filter");
} }
public function testApiAccessWithPOST() public function testApiAccessWithPOST()
{ {
$url = "/api/v1/RestfulServerTest_AuthorRating"; $url = "/api/v1/RestfulServerTest_AuthorRating";
@ -492,6 +492,43 @@ class RestfulServerTest extends SapphireTest
unset($_SERVER['PHP_AUTH_USER']); unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']); unset($_SERVER['PHP_AUTH_PW']);
} }
/** @group wip */
public function testGetWithSortDescending()
{
$url = '/api/v1/RestfulServerTest_Author?sort=FirstName&dir=DESC&fields=FirstName';
$response = Director::test($url);
$results = Convert::xml2array($response->getBody());
$this->assertSame('Author 4', $results['RestfulServerTest_Author'][0]['FirstName']);
$this->assertSame('Author 3', $results['RestfulServerTest_Author'][1]['FirstName']);
$this->assertSame('Author 2', $results['RestfulServerTest_Author'][2]['FirstName']);
$this->assertSame('Author 1', $results['RestfulServerTest_Author'][3]['FirstName']);
}
/** @group wip */
public function testGetWithSortAscending()
{
$url = '/api/v1/RestfulServerTest_Author?sort=FirstName&dir=ASC&fields=FirstName';
$response = Director::test($url);
$results = Convert::xml2array($response->getBody());
$this->assertSame('Author 1', $results['RestfulServerTest_Author'][0]['FirstName']);
$this->assertSame('Author 2', $results['RestfulServerTest_Author'][1]['FirstName']);
$this->assertSame('Author 3', $results['RestfulServerTest_Author'][2]['FirstName']);
$this->assertSame('Author 4', $results['RestfulServerTest_Author'][3]['FirstName']);
}
/** @group wip */
public function testGetSortsByIdWhenInvalidSortColumnIsProvided()
{
$url = '/api/v1/RestfulServerTest_Author?sort=Surname&dir=DESC&fields=FirstName';
$response = Director::test($url);
$results = Convert::xml2array($response->getBody());
$this->assertSame('Author 1', $results['RestfulServerTest_Author'][0]['FirstName']);
$this->assertSame('Author 2', $results['RestfulServerTest_Author'][1]['FirstName']);
$this->assertSame('Author 3', $results['RestfulServerTest_Author'][2]['FirstName']);
$this->assertSame('Author 4', $results['RestfulServerTest_Author'][3]['FirstName']);
}
} }
/** /**
@ -502,17 +539,17 @@ class RestfulServerTest extends SapphireTest
class RestfulServerTest_Comment extends DataObject implements PermissionProvider,TestOnly class RestfulServerTest_Comment extends DataObject implements PermissionProvider,TestOnly
{ {
public static $api_access = true; public static $api_access = true;
public static $db = array( public static $db = array(
"Name" => "Varchar(255)", "Name" => "Varchar(255)",
"Comment" => "Text" "Comment" => "Text"
); );
public static $has_one = array( public static $has_one = array(
'Page' => 'RestfulServerTest_Page', 'Page' => 'RestfulServerTest_Page',
'Author' => 'RestfulServerTest_Author', 'Author' => 'RestfulServerTest_Author',
); );
public function providePermissions() public function providePermissions()
{ {
return array( return array(
@ -521,22 +558,22 @@ class RestfulServerTest_Comment extends DataObject implements PermissionProvider
'DELETE_Comment' => 'Delete Comment Objects', 'DELETE_Comment' => 'Delete Comment Objects',
); );
} }
public function canView($member = null) public function canView($member = null)
{ {
return true; return true;
} }
public function canEdit($member = null) public function canEdit($member = null)
{ {
return Permission::checkMember($member, 'EDIT_Comment'); return Permission::checkMember($member, 'EDIT_Comment');
} }
public function canDelete($member = null) public function canDelete($member = null)
{ {
return Permission::checkMember($member, 'DELETE_Comment'); return Permission::checkMember($member, 'DELETE_Comment');
} }
public function canCreate($member = null) public function canCreate($member = null)
{ {
return Permission::checkMember($member, 'CREATE_Comment'); return Permission::checkMember($member, 'CREATE_Comment');
@ -546,16 +583,16 @@ class RestfulServerTest_Comment extends DataObject implements PermissionProvider
class RestfulServerTest_SecretThing extends DataObject implements TestOnly,PermissionProvider class RestfulServerTest_SecretThing extends DataObject implements TestOnly,PermissionProvider
{ {
public static $api_access = true; public static $api_access = true;
public static $db = array( public static $db = array(
"Name" => "Varchar(255)", "Name" => "Varchar(255)",
); );
public function canView($member = null) public function canView($member = null)
{ {
return Permission::checkMember($member, 'VIEW_SecretThing'); return Permission::checkMember($member, 'VIEW_SecretThing');
} }
public function providePermissions() public function providePermissions()
{ {
return array( return array(
@ -567,20 +604,20 @@ class RestfulServerTest_SecretThing extends DataObject implements TestOnly,Permi
class RestfulServerTest_Page extends DataObject implements TestOnly class RestfulServerTest_Page extends DataObject implements TestOnly
{ {
public static $api_access = false; public static $api_access = false;
public static $db = array( public static $db = array(
'Title' => 'Text', 'Title' => 'Text',
'Content' => 'HTMLText', 'Content' => 'HTMLText',
); );
public static $has_one = array( public static $has_one = array(
'Author' => 'RestfulServerTest_Author', 'Author' => 'RestfulServerTest_Author',
); );
public static $has_many = array( public static $has_many = array(
'TestComments' => 'RestfulServerTest_Comment' 'TestComments' => 'RestfulServerTest_Comment'
); );
public static $belongs_many_many = array( public static $belongs_many_many = array(
'RelatedAuthors' => 'RestfulServerTest_Author', 'RelatedAuthors' => 'RestfulServerTest_Author',
); );
@ -589,21 +626,21 @@ class RestfulServerTest_Page extends DataObject implements TestOnly
class RestfulServerTest_Author extends DataObject implements TestOnly class RestfulServerTest_Author extends DataObject implements TestOnly
{ {
public static $api_access = true; public static $api_access = true;
public static $db = array( public static $db = array(
'Name' => 'Text', 'FirstName' => 'Text',
); );
public static $many_many = array( public static $many_many = array(
'RelatedPages' => 'RestfulServerTest_Page', 'RelatedPages' => 'RestfulServerTest_Page',
'RelatedAuthors' => 'RestfulServerTest_Author', 'RelatedAuthors' => 'RestfulServerTest_Author',
); );
public static $has_many = array( public static $has_many = array(
'PublishedPages' => 'RestfulServerTest_Page', 'PublishedPages' => 'RestfulServerTest_Page',
'Ratings' => 'RestfulServerTest_AuthorRating', 'Ratings' => 'RestfulServerTest_AuthorRating',
); );
public function canView($member = null) public function canView($member = null)
{ {
return true; return true;
@ -622,28 +659,28 @@ class RestfulServerTest_AuthorRating extends DataObject implements TestOnly
'Rating' 'Rating'
) )
); );
public static $db = array( public static $db = array(
'Rating' => 'Int', 'Rating' => 'Int',
'SecretField' => 'Text', 'SecretField' => 'Text',
'WriteProtectedField' => 'Text', 'WriteProtectedField' => 'Text',
); );
public static $has_one = array( public static $has_one = array(
'Author' => 'RestfulServerTest_Author', 'Author' => 'RestfulServerTest_Author',
'SecretRelation' => 'RestfulServerTest_Author', 'SecretRelation' => 'RestfulServerTest_Author',
); );
public function canView($member = null) public function canView($member = null)
{ {
return true; return true;
} }
public function canEdit($member = null) public function canEdit($member = null)
{ {
return true; return true;
} }
public function canCreate($member = null) public function canCreate($member = null)
{ {
return true; return true;

View File

@ -46,7 +46,7 @@ RestfulServerTest_Author:
author2: author2:
FirstName: Author 2 FirstName: Author 2
author3: author3:
Firstname: Author 3 FirstName: Author 3
author4: author4:
FirstName: Author 4 FirstName: Author 4
RelatedAuthors: =>RestfulServerTest_Author.author2,=>RestfulServerTest_Author.author3 RelatedAuthors: =>RestfulServerTest_Author.author2,=>RestfulServerTest_Author.author3
@ -63,4 +63,4 @@ RestfulServerTest_AuthorRating:
SecretRelation: =>RestfulServerTest_Author.author1 SecretRelation: =>RestfulServerTest_Author.author1
RestfulServerTest_SecretThing: RestfulServerTest_SecretThing:
thing1: thing1:
Name: Unspeakable Name: Unspeakable