Commit efde0b8f authored by Matthias Adrowski's avatar Matthias Adrowski
Browse files

[FEATURE] Return certain Exceptions as response with correct header

Instead of throwing exceptions, which will be logged by the system,
return usable status codes and messages.
Will return a valid JSON with a Message key in that Case.

Exceptions, which do not carry codes of 401, 403 or 404 are not
catched for now, stopping us to return too usefull informations
parent 7588e5d3
......@@ -34,6 +34,7 @@ use SGalinski\SgRest\Service\Authentication\AuthenticationServiceInterface;
use SGalinski\SgRest\Service\Authentication\BasicAuthenticationService;
use SGalinski\SgRest\Service\Authentication\BearerAuthenticationService;
use SGalinski\SgRest\Service\RegistrationService;
use TYPO3\CMS\Core\Http\Response;
/**
* This class provides the basic prerequisites for the REST middlewares. New middlewares ideally extend this class
......@@ -56,6 +57,15 @@ abstract class AbstractRestMiddleware implements LoggerAwareInterface, Middlewar
*/
public const restPageType = 1595576052;
/**
* Array of Exception Codes that we will return as proper JSON response
*
* @var int[]
*/
protected $reasonableExceptionCodes = [
401, 403, 404
];
/**
* This array contains all information about the request header as associative array.
*
......@@ -152,10 +162,37 @@ abstract class AbstractRestMiddleware implements LoggerAwareInterface, Middlewar
* @throws Exception
*/
protected function getCallableActionName($method): string {
if($method === 'POST'){
return 'post'.mb_strtoupper(mb_substr($this->pathSegments['verb'], 0, 1)) . mb_substr($this->pathSegments['verb'], 1);
if ($method === 'POST') {
return 'post' . mb_strtoupper(mb_substr($this->pathSegments['verb'], 0, 1)) . mb_substr(
$this->pathSegments['verb'], 1
);
}
return 'get'.mb_strtoupper(mb_substr($this->pathSegments['verb'], 0, 1)) . mb_substr($this->pathSegments['verb'], 1);
return 'get' . mb_strtoupper(mb_substr($this->pathSegments['verb'], 0, 1)) . mb_substr(
$this->pathSegments['verb'], 1
);
}
/**
* @param array|null $configuration
* @param int $statusCode
* @return Response
*/
protected function createExceptionJsonResponse($data, int $statusCode): Response {
if (is_string($data)) {
$message = $data;
$data = [];
$data['message'] = $message;
}
$response = (new Response())
->withStatus($statusCode)
->withHeader('Content-Type', 'application/json; charset=utf-8');
if (!empty($data)) {
$options = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES;
$response->getBody()->write(json_encode($data ?: NULL, $options));
$response->getBody()->rewind();
}
return $response;
}
}
......@@ -69,12 +69,28 @@ class RestAuthenticator extends AbstractRestMiddleware {
$this->requestHeaders = $request->getHeaders();
// Get class name and the action name of the requested rest controller
$this->pathSegments = PathUtility::analyseRequestSegments($this->requestSegments);
try {
$this->pathSegments = PathUtility::analyseRequestSegments($this->requestSegments);
} catch (\RuntimeException $exception) {
if (in_array($exception->getCode(), $this->reasonableExceptionCodes, TRUE)) {
return $this->createExceptionJsonResponse($exception->getMessage(), $exception->getCode());
}
throw $exception;
}
$apiKey = $this->pathSegments['apiKey'];
$identifier = $this->pathSegments['identifier'];
$httpPermissions = $this->registrationService->getHttpPermissionsForEntity(
$this->pathSegments['entity'], $apiKey
);
try {
// @todo: this currently returns a 500 for a permission question?
$httpPermissions = $this->registrationService->getHttpPermissionsForEntity(
$this->pathSegments['entity'], $apiKey
);
} catch (\RuntimeException $exception) {
if (in_array($exception->getCode(), $this->reasonableExceptionCodes, TRUE)) {
return $this->createExceptionJsonResponse($exception->getMessage(), $exception->getCode());
}
throw $exception;
}
$this->accessGroup = $this->registrationService->getAccessGroupByApiKey($apiKey);
$className = $this->getClassName();
......@@ -102,7 +118,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
);
}
throw new \RuntimeException('Auth-token was not provided or was invalid.', 401);
return $this->createExceptionJsonResponse('Auth-token was not provided or was invalid.', 401);
}
$apiKey = $this->requestSegments[0];
......@@ -122,7 +138,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
);
}
throw new \RuntimeException(
return $this->createExceptionJsonResponse(
'You tried to access an object where you do not have the necessary permissions.', 403
);
}
......@@ -145,7 +161,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
);
}
throw new \RuntimeException('The DELETE method is not permitted for the given path.', 405);
return $this->createExceptionJsonResponse('The DELETE method is not permitted for the given path.', 405);
}
if (
......@@ -170,7 +186,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
);
}
throw new \RuntimeException('The ' . $httpMethod . ' method is not permitted for the given path.', 405);
return $this->createExceptionJsonResponse('The ' . $httpMethod . ' method is not permitted for the given path.', 405);
}
if ($httpMethod === 'POST') {
......@@ -194,7 +210,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
);
}
throw new \RuntimeException('The requested path does not exist.', 404);
return $this->createExceptionJsonResponse('The requested path does not exist.', 404);
}
if ($actionName !== '' && !method_exists($className, $actionName . 'Action')) {
......@@ -212,7 +228,7 @@ class RestAuthenticator extends AbstractRestMiddleware {
]
);
throw new \RuntimeException('The requested path does not exist.', 404);
return $this->createExceptionJsonResponse('The requested path does not exist.', 404);
}
}
......
......@@ -74,7 +74,14 @@ class RestDispatcher extends AbstractRestMiddleware {
$this->requestHeaders = $request->getHeaders();
// Get class name and the action name of the requested rest controller
$this->pathSegments = PathUtility::analyseRequestSegments($this->requestSegments);
try {
$this->pathSegments = PathUtility::analyseRequestSegments($this->requestSegments);
} catch (\RuntimeException $exception) {
if (in_array($exception->getCode(), $this->reasonableExceptionCodes, TRUE)) {
return $this->createExceptionJsonResponse($exception->getMessage(), $exception->getCode());
}
throw $exception;
}
$apiKey = $this->pathSegments['apiKey'];
$identifier = $this->pathSegments['identifier'];
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment