-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a7dde87
Showing
6 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace ErrorReport2; | ||
|
||
use SPFW\system\storage\SuperStorage; | ||
|
||
|
||
/** | ||
* ErrorReport2 Model | ||
* | ||
* @package ErrorReport2 | ||
* @version 2.0.0 | ||
*/ | ||
final class ErrorReport2 extends SuperStorage | ||
{} | ||
|
||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
<?php | ||
|
||
namespace ErrorReport2; | ||
|
||
use SPFW\system\config\Config; | ||
use SPFW\system\Controller; | ||
use SPFW\system\JsonOutput; | ||
use SPFW\system\routing\PostRequest; | ||
use SPFW\system\routing\Request; | ||
|
||
|
||
/** | ||
* ErrorReport2 Server | ||
* | ||
* @package ErrorReport2 | ||
* @version 2.0.0 | ||
*/ | ||
final class ErrorReport2Server extends Controller | ||
{ | ||
private const ER2_VERSION = '2.0.0'; | ||
|
||
private const ERROR_RESPONSE_CODE = 400; | ||
private const SUCCESS_RESPONSE_CODE = 201; | ||
|
||
private const REQUIRED_NODES = [ | ||
'authentication', | ||
'general', | ||
'environment', | ||
'request', | ||
'database', | ||
'cookies', | ||
'get', | ||
'post', | ||
'session' | ||
]; | ||
|
||
|
||
private ErrorReport2ServerConfig $config; | ||
|
||
|
||
public function __construct(string $method_name, Request $request) | ||
{ | ||
parent::__construct( $method_name, $request); | ||
|
||
$global_config = Config::get(); | ||
if ($global_config === null) { | ||
throw new \RuntimeException('Unknown global configuration'); | ||
} | ||
|
||
$config_traits = class_uses($global_config); | ||
if (!\in_array(ErrorReport2ServerConfigTrait::class, $config_traits, true)) { | ||
throw new \RuntimeException('Unknown global configuration'); | ||
} | ||
|
||
/** @noinspection PhpUndefinedMethodInspection */ | ||
$er2_server_config = $global_config->getER2Config(); | ||
|
||
if ($er2_server_config === null) { | ||
throw new \RuntimeException('Undefined ER2 configuration'); | ||
} | ||
|
||
$this->config = $er2_server_config; | ||
} | ||
|
||
private function checkHttpMethod(Request $request) : void | ||
{ | ||
if (!($request instanceof PostRequest)) { | ||
throw new \RuntimeException('Invalid http method'); | ||
} | ||
} | ||
|
||
private function checkToken(array $json_structure) : void | ||
{ | ||
$token_string = $json_structure['authentication']['token']; | ||
|
||
if (!$this->config->authenticateToken($token_string)) { | ||
throw new \InvalidArgumentException('Invalid token'); | ||
} | ||
} | ||
|
||
private function returnError() : JsonOutput | ||
{ | ||
$error_json = [ | ||
'logging_accepted' => false | ||
]; | ||
|
||
return new JsonOutput($error_json, self::ERROR_RESPONSE_CODE); | ||
} | ||
|
||
private function returnSuccess() : JsonOutput | ||
{ | ||
$error_json = [ | ||
'logging_accepted' => true | ||
]; | ||
|
||
return new JsonOutput($error_json, self::SUCCESS_RESPONSE_CODE); | ||
} | ||
|
||
public function listener(Request $request) : JsonOutput | ||
{ | ||
try { | ||
$this->checkHttpMethod($request); | ||
$raw_post_payload = $this->rawPostData(); | ||
$json_structure = $this->unpackPayload($raw_post_payload); | ||
$this->validateJson($json_structure); | ||
$this->checkToken($json_structure); | ||
$this->save($json_structure); | ||
} catch (\Throwable $e) { | ||
return $this->returnError(); | ||
} | ||
|
||
return $this->returnSuccess(); | ||
} | ||
|
||
private function prepareJsonInsertion(array $json_structure) : ?string | ||
{ | ||
if ($json_structure === null) { | ||
return null; | ||
} | ||
|
||
try { | ||
return json_encode($json_structure, JSON_THROW_ON_ERROR); | ||
} catch (\JsonException $e) { | ||
return 'JSON ERROR'; | ||
} | ||
} | ||
|
||
private function rawPostData() : string | ||
{ | ||
return file_get_contents('php://input'); | ||
} | ||
|
||
private function save(array $json_structure) : ErrorReport2 | ||
{ | ||
$er2_data = [ | ||
'service_id' => $json_structure['authentication']['service_id'], | ||
'er2_client_version' => $json_structure['authentication']['er2_version'], | ||
'er2_server_version' => self::ER2_VERSION, | ||
'session_id' => $json_structure['general']['er2_session_id'], | ||
'client_timestamp' => \DateTime::createFromFormat('Y-m-d\TH:i:s', $json_structure['general']['timestamp']), | ||
'host_name' => $json_structure['general']['host_name'], | ||
'host_os' => $json_structure['general']['host_os'], | ||
'host_os_release' => $json_structure['general']['host_os_release'], | ||
'host_os_version' => $json_structure['general']['host_os_version'], | ||
'php_version' => $json_structure['general']['php_version'], | ||
'php_mode' => $json_structure['general']['php_mode'], | ||
'php_mem_usage' => $json_structure['general']['php_mem_usage'], | ||
'debug_mode' => $json_structure['general']['debug_mode'], | ||
'request_method' => $json_structure['request']['method'], | ||
'request_domain' => $json_structure['request']['domain'], | ||
'request_subdomain' => $json_structure['request']['subdomain'], | ||
'request_tcp_port' => $json_structure['request']['tcp_port'], | ||
'request_path' => $json_structure['request']['path'], | ||
'request_cli' => $json_structure['request']['cli'], | ||
'request_secure_connection' => $json_structure['request']['secure_connection'], | ||
'environment' => $this->prepareJsonInsertion($json_structure['environment']), | ||
'database' => $this->prepareJsonInsertion($json_structure['database']), | ||
'cookies' => $this->prepareJsonInsertion($json_structure['cookies']), | ||
'get' => $this->prepareJsonInsertion($json_structure['get']), | ||
'post' => $this->prepareJsonInsertion($json_structure['post']), | ||
'session' => $this->prepareJsonInsertion($json_structure['session']), | ||
'errors' => $this->prepareJsonInsertion($json_structure['errors'] ?? null), | ||
'throwable' => $this->prepareJsonInsertion($json_structure['throwable'] ?? null), | ||
]; | ||
|
||
return ErrorReport2::create($er2_data); | ||
} | ||
|
||
/** | ||
* @param string $raw_payload | ||
* | ||
* @return array Decoded json-string | ||
* @throws \JsonException | ||
*/ | ||
private function unpackPayload(string $raw_payload) : array | ||
{ | ||
return json_decode($raw_payload, true, 20, JSON_THROW_ON_ERROR); | ||
} | ||
|
||
private function validateJson(array $json_structure) : void | ||
{ | ||
foreach (self::REQUIRED_NODES as $node_name) { | ||
if (!\array_key_exists($node_name, $json_structure)) { | ||
throw new \InvalidArgumentException('Missing node "' . $node_name . '" in json-structure'); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
namespace ErrorReport2; | ||
|
||
use SPFW\system\config\IConfig; | ||
|
||
|
||
/** | ||
* ErrorReport2 Server Configuration | ||
* | ||
* @package ErrorReport2 | ||
* @version 2.0.0 | ||
*/ | ||
final class ErrorReport2ServerConfig implements IConfig | ||
{ | ||
/** @var string[] $allowed_token */ | ||
private array $allowed_token = []; | ||
|
||
|
||
public function addToken(string $token) : self | ||
{ | ||
$this->allowed_token[] = $token; | ||
return $this; | ||
} | ||
|
||
public function authenticateToken(string $token) : bool | ||
{ | ||
return \in_array($token, $this->allowed_token, true); | ||
} | ||
|
||
public function checkConfig(bool $strict = false) : bool | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace ErrorReport2; | ||
|
||
|
||
/** | ||
* ErrorReport2 Config Trait | ||
* | ||
* @package ErrorReport2 | ||
* @version 2.0.0 | ||
*/ | ||
trait ErrorReport2ServerConfigTrait | ||
{ | ||
private ?ErrorReport2ServerConfig $er2_config; | ||
|
||
final public function getER2Config() : ?ErrorReport2ServerConfig | ||
{ | ||
return $this->er2_config; | ||
} | ||
|
||
final public function setER2Config(ErrorReport2ServerConfig $er2_config) : void | ||
{ | ||
$this->er2_config = $er2_config; | ||
} | ||
} | ||
|
||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Error Report 2 (Server) | ||
|
||
An error reporting service for SPFW. | ||
Clients send error and exception logs to an API. | ||
|
||
## Requirements | ||
|
||
* SPFW >=1.3.0, | ||
* PHP >=7.4 and | ||
* MySQL >= 5.7 (or Maria DB >= 10.1). | ||
|
||
__Note:__ | ||
While the client requires a minimum SPFW version 1.0.0, the serverside requires SPFW 1.3.0 or newer. | ||
|
||
## Installation | ||
|
||
1. Include this git-project as submodule in your modules-directory, | ||
2. execute the sql-batch file _er2.sql_ in your database, | ||
3. extend your config with ErrorReport2ServerConfigTrait, | ||
4. add a ErrorReport2ServerConfig to your global config, | ||
5. add a route for the listener to your routing repository and | ||
6. configure ER2 Server (e.g. set up access token). | ||
|
||
For step 3, a customizable config must exist. | ||
If it does not exist yet, create a new class for your application config. | ||
Extend SPFW\config\Config class. | ||
Extend class by using _use_ keyword for ErrorReport2ServerConfigTrait. | ||
Then replace SPFW\config\Config class by new application config class in _config.php_ file. | ||
|
||
It is recommended to allow only secure connections. | ||
This must be set in your web servers configuration. | ||
|
||
__Example for 1.:__ | ||
``` | ||
cd src/modules | ||
git submodule add URL_TO_REPOSITORY | ||
``` | ||
|
||
__Example for 5.:__ | ||
|
||
``` | ||
Routing::addRoute((new StaticRoute(\ErrorReport2\ErrorReport2Server::class, 'listener')) | ||
->setFile('/er2_listener')); | ||
``` | ||
|
||
__Example for 6.:__ | ||
|
||
``` | ||
$config->setER2Config((new \ErrorReport2\ErrorReport2ServerConfig())->addToken('3915753d8765a0')); | ||
``` | ||
|
||
## Configuration | ||
|
||
### Required configuration | ||
|
||
Every connection requires an access token. | ||
It is recommended to create an individual token for each service you want to connect. | ||
|
||
### Optional configuration | ||
|
||
There are no optional parameter yet. | ||
|
||
## Future development | ||
|
||
A parallel project will be started to offer a web-gui for viewing and analyzing ER2 reports. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
-- Error Report 2 Module | ||
-- Version 2.0.0 | ||
|
||
|
||
CREATE TABLE ErrorReport2 ( | ||
`id` bigint unsigned NOT NULL auto_increment PRIMARY KEY, | ||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
`service_id` varchar(50), | ||
`er2_client_version` varchar(11) NOT NULL, | ||
`er2_server_version` varchar(11) NOT NULL, | ||
`session_id` varchar(128) NOT NULL, | ||
`client_timestamp` datetime NOT NULL, | ||
`host_name` varchar(100) NOT NULL, | ||
`host_os` varchar(100) NOT NULL, | ||
`host_os_release` varchar(100) NOT NULL, | ||
`host_os_version` varchar(100) NOT NULL, | ||
`php_version` varchar(20) NOT NULL, | ||
`php_mode` varchar(30) NOT NULL, | ||
`php_mem_usage` bigint unsigned NOT NULL, | ||
`debug_mode` tinyint unsigned NOT NULL COMMENT 'BOOL', | ||
`request_method` enum('CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE') NOT NULL, | ||
`request_domain` varchar(255) NOT NULL, | ||
`request_subdomain` varchar(255) NOT NULL, | ||
`request_tcp_port` smallint unsigned NOT NULL, | ||
`request_path` varchar(255) NOT NULL, | ||
`request_cli` tinyint unsigned NOT NULL COMMENT 'BOOL', | ||
`request_secure_connection` tinyint unsigned NOT NULL COMMENT 'BOOL', | ||
`general` text NOT NULL, | ||
`environment` text, | ||
`database` text, | ||
`cookies` text, | ||
`get` text, | ||
`post` text, | ||
`session` text, | ||
`errors` text, | ||
`throwable` text | ||
) ENGINE = InnoDB; |