Skip to content
Snippets Groups Projects
Commit 615c8e36 authored by Markus Guenther's avatar Markus Guenther
Browse files

[FEATURE] Add request logging

parent fd3ff9bb
No related branches found
No related tags found
No related merge requests found
......@@ -29,6 +29,7 @@ namespace SGalinski\SgRest;
use SGalinski\SgRest\Connector\ExtBaseConnector;
use SGalinski\SgRest\Service\DataResolveService;
use SGalinski\SgRest\Utility\PathUtility;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\TimeTracker\NullTimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
......@@ -77,6 +78,11 @@ class Dispatcher {
*/
protected $registrationService;
/**
* @var \TYPO3\CMS\Core\Log\Logger
*/
protected $logger;
/**
* Constructor
*
......@@ -94,6 +100,7 @@ class Dispatcher {
$serviceClassPath = 'SGalinski\SgRest\Service\\';
$this->registrationService = $this->objectManager->get($serviceClassPath . 'RegistrationService');
$this->authenticationService = $this->objectManager->get($serviceClassPath . 'AuthenticationService');
$this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__);
$this->initFrontend();
}
......@@ -123,12 +130,29 @@ class Dispatcher {
$authenticated = $this->authenticationService->verifyRequest($this->requestHeaders);
if (!$authenticated) {
$this->logRequest(
'Auth-token was not provided or was invalid.',
[
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders
],
LogLevel::ERROR
);
throw new \Exception('Auth-token was not provided or was invalid.', 401);
}
$apiKey = $this->requestSegments[0];
$verifiedAccess = $this->authenticationService->verifyUserAccess($apiKey);
if (!$verifiedAccess) {
$this->logRequest(
'You tried to access an object where you do not have the necessary permissions.',
[
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders
],
LogLevel::ERROR
);
throw new \Exception('You tried to access an object where you do not have the necessary permissions.', 403);
}
......@@ -145,6 +169,18 @@ class Dispatcher {
$httpMethod = $this->getMethod();
if ($httpMethod === 'DELETE' && $this->pathSegments['verb'] !== '' && !$httpPermissions['deleteForVerbs']) {
$this->logRequest(
'The DELETE method is not permitted for the given path.',
[
'httpMethod' => $identifier,
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders,
'className' => $className,
'actionName' => $actionName
],
LogLevel::ERROR
);
throw new \Exception('The DELETE method is not permitted for the given path.', 405);
}
......@@ -153,6 +189,18 @@ class Dispatcher {
($httpMethod === 'PATCH' && $identifier <= 0 && !$httpPermissions['patchWithIdentifier']) ||
($httpMethod === 'POST' && $identifier > 0 && !$httpPermissions['postWithIdentifier'])
) {
$this->logRequest(
'The ' . $httpMethod . ' method is not permitted for the given path.',
[
'httpMethod' => $identifier,
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders,
'className' => $className,
'actionName' => $actionName
],
LogLevel::ERROR
);
throw new \Exception('The ' . $httpMethod . ' method is not permitted for the given path.', 405);
}
......@@ -161,10 +209,34 @@ class Dispatcher {
}
if (!class_exists($className)) {
$this->logRequest(
'The requested path does not exist.',
[
'httpMethod' => $identifier,
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders,
'className' => $className,
'actionName' => $actionName
],
LogLevel::ERROR
);
throw new \Exception('The requested path does not exist.', 404);
}
if ($actionName !== '' && !method_exists($className, $actionName . 'Action')) {
$this->logRequest(
'The requested path does not exist.',
[
'httpMethod' => $identifier,
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders,
'className' => $className,
'actionName' => $actionName
],
LogLevel::ERROR
);
throw new \Exception('The requested path does not exist.', 404);
}
......@@ -192,6 +264,20 @@ class Dispatcher {
$extbaseConnector->setParameters($parameters);
}
$this->logRequest(
'REST Request before runControllerAction: ',
[
'httpMethod' => $identifier,
'parameters' => $parameters,
'apiKey' => $apiKey,
'requestSegments' => $this->requestSegments,
'requestHeaders' => $this->requestHeaders,
'className' => $className,
'actionName' => $actionName
],
LogLevel::INFO
);
return $extbaseConnector->runControllerAction(
$this->getControllerName(), $this->getActionName(), $this->accessGroup['vendorName'], $apiKey
);
......@@ -343,4 +429,39 @@ class Dispatcher {
return $method;
}
/**
* Wrapper for the log calls. First check if logging is enabled and then pass thru the data to the TYPO3 log API.
*
* @param string $message
* @param array $data
* @param int $severity
*/
protected function logRequest($message, array $data, $severity) {
$extensionConfiguration = $this->getExtensionConfiguration();
if (filter_var($extensionConfiguration['enableLogging'], FILTER_VALIDATE_BOOLEAN)) {
$data = array_merge(
$data,
[
'REQUEST' => $_REQUEST,
'SERVER' => $_SERVER
]
);
$this->logger->log($severity, $message, $data);
}
}
/**
* Returns the extension configuration
*
* @return array
*/
protected function getExtensionConfiguration() {
$configuration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['sg_rest']);
if (!is_array($configuration)) {
return [];
}
return $configuration;
}
}
\ No newline at end of file
# cat=basic/enable; type=boolean; label=Enable enableLogging:Log API requests
enableLogging = 0
......@@ -7,6 +7,13 @@ if (!defined('TYPO3_MODE')) {
/** @noinspection PhpUndefinedVariableInspection */
$extPath = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('sg_rest');
// Configure logger for the services controllers
$GLOBALS['TYPO3_CONF_VARS']['LOG']['SGalinski']['SgRest']['writerConfiguration'] = [
TYPO3\CMS\Core\Log\LogLevel::INFO => [
'TYPO3\\CMS\\Core\\Log\\Writer\\DatabaseWriter' => ['logTable' => 'tx_sgrest_log'],
],
];
// TypoScript configuration
$tsPath = $extPath . 'Configuration/TypoScript/';
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptConstants(
......
......@@ -2,4 +2,19 @@ CREATE TABLE fe_users (
tx_sgrest_auth_token varchar(40) DEFAULT '' NOT NULL,
tx_sgrest_access_groups varchar(255) DEFAULT '' NOT NULL,
tx_sgrest_test_mode tinyint(4) unsigned DEFAULT '0' NOT NULL,
);
\ No newline at end of file
);
CREATE TABLE tx_sgrest_log (
uid int(11) unsigned NOT NULL auto_increment,
pid int(11) unsigned DEFAULT '0' NOT NULL,
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
request_id varchar(13) DEFAULT '' NOT NULL,
time_micro int(11) unsigned DEFAULT '0' NOT NULL,
component varchar(255) DEFAULT '' NOT NULL,
level tinyint(1) unsigned DEFAULT '0' NOT NULL,
message text,
data text,
PRIMARY KEY (uid),
KEY parent (pid),
KEY request (request_id)
);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment