Merge branch 'development' into 'master'
Release 1.2.0 See merge request fuzeworks/webcomponent!2
This commit is contained in:
commit
0ba98f8130
|
@ -14,17 +14,15 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1.0",
|
"php": ">=7.1.0",
|
||||||
"fuzeworks/core": "dev-development",
|
"fuzeworks/mvcr": "~1.2.0",
|
||||||
"fuzeworks/mvcr": "dev-master"
|
"fuzeworks/core": "~1.2.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^7",
|
"phpunit/phpunit": "^7"
|
||||||
"fuzeworks/tracycomponent": "dev-master"
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"FuzeWorks\\": "src/FuzeWorks/"
|
"FuzeWorks\\": "src/FuzeWorks/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -49,56 +49,16 @@ return [
|
||||||
| 'csrf_regenerate' = Regenerate token on every submission
|
| 'csrf_regenerate' = Regenerate token on every submission
|
||||||
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
|
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
|
||||||
*/
|
*/
|
||||||
'csrf_protection' => false,
|
'csrf_protection' => true,
|
||||||
'csrf_token_name' => 'fw_csrf_token',
|
'csrf_token_name' => 'fw_csrf_token',
|
||||||
'csrf_cookie_name' => 'fw_csrf_cookie',
|
|
||||||
'csrf_expire' => 7200,
|
'csrf_expire' => 7200,
|
||||||
'csrf_regenerate' => TRUE,
|
|
||||||
'csrf_exclude_uris' => array(),
|
'csrf_exclude_uris' => array(),
|
||||||
|
|
||||||
/*
|
// CSRF Cookie information
|
||||||
|--------------------------------------------------------------------------
|
'csrf_cookie_name' => 'fw_csrf_cookie',
|
||||||
| Standardize newlines
|
'csrf_cookie_prefix' => '',
|
||||||
|--------------------------------------------------------------------------
|
'csrf_cookie_domain' => '',
|
||||||
|
|
'csrf_cookie_path' => '/',
|
||||||
| Determines whether to standardize newline characters in input data,
|
'csrf_cookie_secure' => false,
|
||||||
| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
|
'csrf_cookie_httponly' => false
|
||||||
|
|
|
||||||
| This is particularly useful for portability between UNIX-based OSes,
|
|
||||||
| (usually \n) and Windows (\r\n).
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'standardize_newlines' => FALSE,
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Global XSS Filtering
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Determines whether the XSS filter is always active when GET, POST or
|
|
||||||
| COOKIE data is encountered
|
|
||||||
|
|
|
||||||
| WARNING: This feature is DEPRECATED and currently available only
|
|
||||||
| for backwards compatibility purposes!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
'global_xss_filtering' => FALSE,
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Reverse Proxy IPs
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| If your server is behind a reverse proxy, you must whitelist the proxy
|
|
||||||
| IP addresses from which CodeIgniter should trust headers such as
|
|
||||||
| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
|
|
||||||
| the visitor's IP address.
|
|
||||||
|
|
|
||||||
| You can use both an array or a comma-separated list of proxy addresses,
|
|
||||||
| as well as specifying whole subnets. Here are a few examples:
|
|
||||||
|
|
|
||||||
| Comma-separated: '10.0.1.200,192.168.5.0/24'
|
|
||||||
| Array: array('10.0.1.200', '192.168.5.0/24')
|
|
||||||
*/
|
|
||||||
'proxy_ips' => ''
|
|
||||||
];
|
];
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FuzeWorks WebComponent.
|
||||||
|
*
|
||||||
|
* The FuzeWorks PHP FrameWork
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013-2019 TechFuze
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author TechFuze
|
||||||
|
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||||
|
* @license https://opensource.org/licenses/MIT MIT License
|
||||||
|
*
|
||||||
|
* @link http://techfuze.net/fuzeworks
|
||||||
|
* @since Version 1.2.0
|
||||||
|
*
|
||||||
|
* @version Version 1.2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FuzeWorks\Event;
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
|
||||||
|
class ResourceServeEvent extends Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $resourceName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $resourceUrlSegments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $resourceFilePath;
|
||||||
|
|
||||||
|
public function init(string $resourceName, array $resourceUrlSegments, string $resourceFilePath)
|
||||||
|
{
|
||||||
|
$this->resourceName = $resourceName;
|
||||||
|
$this->resourceUrlSegments = $resourceUrlSegments;
|
||||||
|
$this->resourceFilePath = $resourceFilePath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FuzeWorks WebComponent.
|
||||||
|
*
|
||||||
|
* The FuzeWorks PHP FrameWork
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013-2019 TechFuze
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author TechFuze
|
||||||
|
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||||
|
* @license https://opensource.org/licenses/MIT MIT License
|
||||||
|
*
|
||||||
|
* @link http://techfuze.net/fuzeworks
|
||||||
|
* @since Version 1.2.0
|
||||||
|
*
|
||||||
|
* @version Version 1.2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FuzeWorks\Event;
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
|
||||||
|
class RouteWebRequestEvent extends Event
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $uriString;
|
||||||
|
|
||||||
|
public function init(string $uriString)
|
||||||
|
{
|
||||||
|
$this->uriString = $uriString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FuzeWorks Component.
|
||||||
|
*
|
||||||
|
* The FuzeWorks PHP FrameWork
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013-2019 TechFuze
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author TechFuze
|
||||||
|
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||||
|
* @license https://opensource.org/licenses/MIT MIT License
|
||||||
|
*
|
||||||
|
* @link http://techfuze.net/fuzeworks
|
||||||
|
* @since Version 1.2.0
|
||||||
|
*
|
||||||
|
* @version Version 1.2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FuzeWorks\Exception;
|
||||||
|
|
||||||
|
|
||||||
|
class CSRFException extends SecurityException
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
<?php /** @noinspection ALL */
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FuzeWorks WebComponent.
|
* FuzeWorks WebComponent.
|
||||||
|
@ -38,8 +38,10 @@
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
|
|
||||||
use FuzeWorks\ConfigORM\ConfigORM;
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
use Tracy\Debugger;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Implement remaining methods from OldInput
|
||||||
|
*/
|
||||||
class Input
|
class Input
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -69,6 +71,13 @@ class Input
|
||||||
// Set the configuration
|
// Set the configuration
|
||||||
$this->webConfig = Factory::getInstance()->config->getConfig('web');
|
$this->webConfig = Factory::getInstance()->config->getConfig('web');
|
||||||
|
|
||||||
|
// If not handling requests, do not continue
|
||||||
|
if (!WebComponent::$willHandleRequest)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Start session
|
||||||
|
session_start();
|
||||||
|
|
||||||
// Sanitize all global arrays
|
// Sanitize all global arrays
|
||||||
$this->sanitizeGlobals();
|
$this->sanitizeGlobals();
|
||||||
|
|
||||||
|
@ -76,8 +85,8 @@ class Input
|
||||||
{
|
{
|
||||||
if (class_exists('\FuzeWorks\TracyComponent', true) && \FuzeWorks\TracyComponent::isEnabled())
|
if (class_exists('\FuzeWorks\TracyComponent', true) && \FuzeWorks\TracyComponent::isEnabled())
|
||||||
{
|
{
|
||||||
set_exception_handler([$this, 'tracyExceptionHandler']);
|
Core::addExceptionHandler([$this, 'restoreGlobalArrays'], Priority::HIGHEST);
|
||||||
set_error_handler([$this, 'tracyErrorHandler']);
|
Core::addErrorHandler([$this, 'restoreGlobalArrays'], Priority::HIGHEST);
|
||||||
}
|
}
|
||||||
Events::addListener(
|
Events::addListener(
|
||||||
[$this, 'restoreGlobalArrays'],
|
[$this, 'restoreGlobalArrays'],
|
||||||
|
@ -87,35 +96,10 @@ class Input
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to restore global arrays before handling errors by Tracy
|
* Restores global arrays before handling by processes outside of FuzeWorks
|
||||||
*
|
*
|
||||||
* @param $exception
|
|
||||||
* @param bool $exit
|
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
public function tracyExceptionHandler($exception, $exit = true)
|
|
||||||
{
|
|
||||||
$this->restoreGlobalArrays();
|
|
||||||
Debugger::exceptionHandler($exception, $exit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to restore global arrays before handling errors by Tracy
|
|
||||||
*
|
|
||||||
* @param $severity
|
|
||||||
* @param $message
|
|
||||||
* @param $file
|
|
||||||
* @param $line
|
|
||||||
* @param array $context
|
|
||||||
* @throws \ErrorException
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
public function tracyErrorHandler($severity, $message, $file, $line, $context = [])
|
|
||||||
{
|
|
||||||
$this->restoreGlobalArrays();
|
|
||||||
Debugger::errorHandler($severity, $message, $file, $line, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function restoreGlobalArrays()
|
public function restoreGlobalArrays()
|
||||||
{
|
{
|
||||||
Logger::logInfo('Restoring global $_GET, $_POST, $_SERVER, $_COOKIE arrays');
|
Logger::logInfo('Restoring global $_GET, $_POST, $_SERVER, $_COOKIE arrays');
|
||||||
|
@ -235,6 +219,8 @@ class Input
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Used to fetch variables from the global arrays
|
||||||
|
*
|
||||||
* @param string $arrayName
|
* @param string $arrayName
|
||||||
* @param null $index
|
* @param null $index
|
||||||
* @param bool $xssClean
|
* @param bool $xssClean
|
||||||
|
@ -242,8 +228,8 @@ class Input
|
||||||
*/
|
*/
|
||||||
protected function getFromInputArray(string $arrayName, $index = null, bool $xssClean = true)
|
protected function getFromInputArray(string $arrayName, $index = null, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
// Clean XSS if requested manually or forced through configuration
|
// Never run XSS clean if disabled by config
|
||||||
$xssClean = $xssClean || $this->webConfig->get('xss_clean');
|
$xssClean = ($this->webConfig->get('xss_clean') == true ? $xssClean : false);
|
||||||
|
|
||||||
// If the index is null, the entire array is requested
|
// If the index is null, the entire array is requested
|
||||||
$index = (!is_null($index) ? $index : array_keys($this->inputArray[$arrayName]));
|
$index = (!is_null($index) ? $index : array_keys($this->inputArray[$arrayName]));
|
||||||
|
@ -266,59 +252,124 @@ class Input
|
||||||
return ($xssClean === true ? $this->security->xss_clean($value) : $value);
|
return ($xssClean === true ? $this->security->xss_clean($value) : $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_GET array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function get($index = null, bool $xssClean = true)
|
public function get($index = null, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('get', $index, $xssClean);
|
return $this->getFromInputArray('get', $index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_POST array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function post($index = null, bool $xssClean = true)
|
public function post($index = null, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('post', $index, $xssClean);
|
return $this->getFromInputArray('post', $index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_POST or $_GET array. Tries POST first
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function postGet($index, bool $xssClean = true)
|
public function postGet($index, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return isset($this->inputArray['post'][$index]) ? $this->post($index, $xssClean) : $this->get($index, $xssClean);
|
return isset($this->inputArray['post'][$index]) ? $this->post($index, $xssClean) : $this->get($index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_GET or $_POST array. Tries GET first
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function getPost($index, bool $xssClean = true)
|
public function getPost($index, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return isset($this->inputArray['get'][$index]) ? $this->get($index, $xssClean) : $this->post($index, $xssClean);
|
return isset($this->inputArray['get'][$index]) ? $this->get($index, $xssClean) : $this->post($index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_COOKIE array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function cookie($index = null, bool $xssClean = true)
|
public function cookie($index = null, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('cookie', $index, $xssClean);
|
return $this->getFromInputArray('cookie', $index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch variables from the global $_SERVER array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function server($index = null, bool $xssClean = true)
|
public function server($index = null, bool $xssClean = true)
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('server', $index, $xssClean);
|
return $this->getFromInputArray('server', $index, $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Extend with OldInput functionality
|
* Fetch the HTTP_USER_AGENT variable from the $_SERVER array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function ip()
|
|
||||||
{
|
|
||||||
$ip = '';
|
|
||||||
// Validate IP
|
|
||||||
|
|
||||||
$valid = (
|
|
||||||
(bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
|
|
||||||
(bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function userAgent(bool $xssClean = true): string
|
public function userAgent(bool $xssClean = true): string
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('server', 'HTTP_USER_AGENT', $xssClean);
|
return $this->getFromInputArray('server', 'HTTP_USER_AGENT', $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the REQUEST_METHOD variable from the $_SERVER array
|
||||||
|
*
|
||||||
|
* @param string|array|null $index
|
||||||
|
* @param bool $xssClean
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function method(bool $xssClean = true): string
|
public function method(bool $xssClean = true): string
|
||||||
{
|
{
|
||||||
return $this->getFromInputArray('server', 'REQUEST_METHOD', $xssClean);
|
return $this->getFromInputArray('server', 'REQUEST_METHOD', $xssClean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is HTTPS?
|
||||||
|
*
|
||||||
|
* Determines if the application is accessed via an encrypted
|
||||||
|
* (HTTPS) connection.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isHttps(): bool
|
||||||
|
{
|
||||||
|
if (!empty($this->inputArray['server']['HTTPS']) && strtolower($this->inputArray['server']['HTTPS']) !== 'off')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
elseif (isset($this->inputArray['server']['HTTP_X_FORWARDED_PROTO']) && $this->inputArray['server']['HTTP_X_FORWARDED_PROTO'] === 'https')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
elseif ( ! empty($this->inputArray['server']['HTTP_FRONT_END_HTTPS']) && strtolower($this->inputArray['server']['HTTP_FRONT_END_HTTPS']) !== 'off')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -38,9 +38,11 @@ namespace FuzeWorks;
|
||||||
|
|
||||||
|
|
||||||
use FuzeWorks\ConfigORM\ConfigORM;
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
use FuzeWorks\Event\HelperLoadEvent;
|
|
||||||
use FuzeWorks\Exception\OutputException;
|
use FuzeWorks\Exception\OutputException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Implement caching
|
||||||
|
*/
|
||||||
class Output
|
class Output
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -51,6 +53,13 @@ class Output
|
||||||
*/
|
*/
|
||||||
private $input;
|
private $input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal URI class
|
||||||
|
*
|
||||||
|
* @var URI
|
||||||
|
*/
|
||||||
|
private $uri;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebCfg
|
* WebCfg
|
||||||
*
|
*
|
||||||
|
@ -82,12 +91,38 @@ class Output
|
||||||
public $mimes = [];
|
public $mimes = [];
|
||||||
protected $mimeType = 'text/html';
|
protected $mimeType = 'text/html';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of time the current page is cached
|
||||||
|
*
|
||||||
|
* @var int $cacheTime
|
||||||
|
*/
|
||||||
|
protected $cacheTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a cache file is being used now
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $usingCache = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status code that will be sent to the client
|
||||||
|
*
|
||||||
|
* @var int $statusCode
|
||||||
|
*/
|
||||||
protected $statusCode = 200;
|
protected $statusCode = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status code text that will be sent along with $statusCode
|
||||||
|
*
|
||||||
|
* @var string $statusText
|
||||||
|
*/
|
||||||
protected $statusText = 'OK';
|
protected $statusText = 'OK';
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
$this->input = Factory::getInstance()->input;
|
$this->input = Factory::getInstance()->input;
|
||||||
|
$this->uri = Factory::getInstance()->uri;
|
||||||
$this->mimes = Factory::getInstance()->config->getConfig('mimes')->toArray();
|
$this->mimes = Factory::getInstance()->config->getConfig('mimes')->toArray();
|
||||||
$this->config = Factory::getInstance()->config->getConfig('web');
|
$this->config = Factory::getInstance()->config->getConfig('web');
|
||||||
|
|
||||||
|
@ -95,6 +130,200 @@ class Output
|
||||||
$this->compressOutput = (!$zlib && $this->config->get('compress_output') && extension_loaded('zlib'));
|
$this->compressOutput = (!$zlib && $this->config->get('compress_output') && extension_loaded('zlib'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display Output
|
||||||
|
*
|
||||||
|
* Processes and sends finalized output data to the browser along
|
||||||
|
* with any server headers.
|
||||||
|
*
|
||||||
|
* @param string $output Output data override
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function display(string $output = null)
|
||||||
|
{
|
||||||
|
// Set the output data
|
||||||
|
$output = is_null($output) ? $this->output : $output;
|
||||||
|
|
||||||
|
// Write cache if requested to do so
|
||||||
|
if ($this->cacheTime > 0)
|
||||||
|
$this->writeCache($output);
|
||||||
|
|
||||||
|
// First send status code
|
||||||
|
http_response_code($this->statusCode);
|
||||||
|
@header('Status: ' . $this->statusCode . ' ' . $this->statusText, true);
|
||||||
|
|
||||||
|
// If compression is requested, start buffering
|
||||||
|
if (
|
||||||
|
$this->compressOutput && !$this->usingCache &&
|
||||||
|
!is_null($this->input->server('HTTP_ACCEPT_ENCODING')) &&
|
||||||
|
strpos($this->input->server('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Logger::log("Compressing output...");
|
||||||
|
ob_start('ob_gzhandler');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send gzip headers when using cache
|
||||||
|
if ($this->usingCache && $this->compressOutput)
|
||||||
|
{
|
||||||
|
if (!is_null($this->input->server('HTTP_ACCEPT_ENCODING')) &&
|
||||||
|
strpos($this->input->server('HTTP_ACCEPT_ENCODING'), 'gzip') !== false)
|
||||||
|
{
|
||||||
|
header('Content-Encoding: gzip');
|
||||||
|
header('Content-Length: '.strlen($output));
|
||||||
|
}
|
||||||
|
// If the cache is zipped, but the client doesn't support it, decompress the output
|
||||||
|
else
|
||||||
|
$output = gzinflate(substr($output, 10, -8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send all available headers
|
||||||
|
if (!empty($this->headers))
|
||||||
|
foreach ($this->headers as $header)
|
||||||
|
@header($header[0], $header[1]);
|
||||||
|
|
||||||
|
echo $output;
|
||||||
|
|
||||||
|
Logger::log('Output sent to browser');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the current page to be cached
|
||||||
|
*
|
||||||
|
* Set the amount of time with the $time parameter.
|
||||||
|
*
|
||||||
|
* @param int $time In minutes
|
||||||
|
*/
|
||||||
|
public function cache(int $time)
|
||||||
|
{
|
||||||
|
$this->cacheTime = $time > 0 ? $time : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCache(string $selector): bool
|
||||||
|
{
|
||||||
|
// If empty, index page is requested
|
||||||
|
$selector = empty($selector) ? 'index' : $selector;
|
||||||
|
|
||||||
|
// Generate the full uri
|
||||||
|
$uri = $this->config->get('base_url') . $selector;
|
||||||
|
|
||||||
|
// Determine the file that holds the cache
|
||||||
|
if ($this->compressOutput)
|
||||||
|
$file = Core::$tempDir . DS . 'OutputCache' . DS . md5($uri) . '_gzip.fwcache';
|
||||||
|
else
|
||||||
|
$file = Core::$tempDir . DS . 'OutputCache' . DS . md5($uri) . '.fwcache';
|
||||||
|
|
||||||
|
|
||||||
|
// Determine if file exists
|
||||||
|
if (!file_exists($file))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve cache
|
||||||
|
$cache = file_get_contents($file);
|
||||||
|
|
||||||
|
// Verify that this is a cache file
|
||||||
|
if (!preg_match('/^(.*)EndFuzeWorksCache--->/', $cache, $match))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve data from cache file
|
||||||
|
$cacheInfo = unserialize($match[1]);
|
||||||
|
|
||||||
|
// Test if the cache has expired
|
||||||
|
if (time() > $cacheInfo['expire'])
|
||||||
|
{
|
||||||
|
// If not writeable, log warning and do not remove
|
||||||
|
if (!Core::isReallyWritable($file))
|
||||||
|
{
|
||||||
|
Logger::logWarning("Found expired output cache. Could not remove!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete file if expired
|
||||||
|
@unlink($file);
|
||||||
|
Logger::logInfo("Found expired output cache. Removed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Send cache header
|
||||||
|
|
||||||
|
// Send all the headers cached in the file
|
||||||
|
foreach ($cacheInfo['headers'] as $header)
|
||||||
|
$this->setHeader($header[0], $header[1]);
|
||||||
|
|
||||||
|
// And save the output
|
||||||
|
$this->usingCache = true;
|
||||||
|
$this->setOutput(substr($cache, strlen($match[0])));
|
||||||
|
Logger::logInfo("Found output cache. Set output.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeCache(string $output)
|
||||||
|
{
|
||||||
|
// First create cache directory
|
||||||
|
$cachePath = Core::$tempDir . DS . 'OutputCache';
|
||||||
|
|
||||||
|
// Attempt to create the OutputCache directory in the TempDirectory
|
||||||
|
if (!is_dir($cachePath) && !mkdir($cachePath, 0777, false))
|
||||||
|
{
|
||||||
|
Logger::logError("Could not write output cache. Cannot create directory. Are permissions set correctly?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If directory is not writable, return error
|
||||||
|
if (!Core::isReallyWritable($cachePath))
|
||||||
|
{
|
||||||
|
Logger::logError("Could not write output cache. No file permissions. Are permissions set correctly?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the full uri
|
||||||
|
$uri = $this->config->get('base_url') . (empty($this->uri->uriString()) ? 'index' : $this->uri->uriString());
|
||||||
|
|
||||||
|
// Determine the file that holds the cache
|
||||||
|
if ($this->compressOutput)
|
||||||
|
$file = $cachePath . DS . md5($uri) . '_gzip.fwcache';
|
||||||
|
else
|
||||||
|
$file = $cachePath . DS . md5($uri) . '.fwcache';
|
||||||
|
|
||||||
|
|
||||||
|
// If compression is enabled, compress the output
|
||||||
|
if ($this->compressOutput)
|
||||||
|
{
|
||||||
|
$output = gzencode($output);
|
||||||
|
if ($this->getHeader('content-type') === null)
|
||||||
|
$this->setContentType($this->mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate expiry time
|
||||||
|
$expire = time() + ($this->cacheTime * 60);
|
||||||
|
|
||||||
|
// Prepare the cache contents
|
||||||
|
$cache = [
|
||||||
|
'expire' => $expire,
|
||||||
|
'headers' => $this->headers
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create cache file contents
|
||||||
|
$cache = serialize($cache) . 'EndFuzeWorksCache--->' . $output;
|
||||||
|
|
||||||
|
// Write the cache
|
||||||
|
if (file_put_contents($file, $cache, LOCK_EX) === false)
|
||||||
|
{
|
||||||
|
@unlink($file);
|
||||||
|
Logger::logError("Could not write output cache. File error. Deleting cache file.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lowering permissions to read only
|
||||||
|
chmod($cachePath, 0640);
|
||||||
|
|
||||||
|
// And report back
|
||||||
|
Logger::logInfo("Output cache has been saved.");
|
||||||
|
|
||||||
|
// @todo Set cache header
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Output
|
* Get Output
|
||||||
*
|
*
|
||||||
|
@ -132,45 +361,6 @@ class Output
|
||||||
$this->output .= $output;
|
$this->output .= $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display Output
|
|
||||||
*
|
|
||||||
* Processes and sends finalized output data to the browser along
|
|
||||||
* with any server headers.
|
|
||||||
*
|
|
||||||
* @param string $output Output data override
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function display(string $output = null)
|
|
||||||
{
|
|
||||||
// Set the output data
|
|
||||||
$output = is_null($output) ? $this->output : $output;
|
|
||||||
|
|
||||||
// First send status code
|
|
||||||
http_response_code($this->statusCode);
|
|
||||||
@header('Status: ' . $this->statusCode . ' ' . $this->statusText, true);
|
|
||||||
|
|
||||||
// If compression is requested, start buffering
|
|
||||||
if (
|
|
||||||
$this->compressOutput &&
|
|
||||||
!is_null($this->input->server('HTTP_ACCEPT_ENCODING')) &&
|
|
||||||
strpos($this->input->server('HTTP_ACCEPT_ENCODING'), 'gzip') !== false
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Logger::log("Compressing output...");
|
|
||||||
ob_start('ob_gzhandler');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send all available headers
|
|
||||||
if (!empty($this->headers))
|
|
||||||
foreach ($this->headers as $header)
|
|
||||||
@header($header[0], $header[1]);
|
|
||||||
|
|
||||||
echo $output;
|
|
||||||
|
|
||||||
Logger::log('Output sent to browser');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set Header
|
* Set Header
|
||||||
*
|
*
|
||||||
|
@ -181,6 +371,11 @@ class Output
|
||||||
*/
|
*/
|
||||||
public function setHeader(string $header, bool $replace = true)
|
public function setHeader(string $header, bool $replace = true)
|
||||||
{
|
{
|
||||||
|
// If compression is enabled content-length should be suppressed, since it won't match the length
|
||||||
|
// of the compressed output.
|
||||||
|
if ($this->compressOutput && strncasecmp($header, 'content-length', 14) === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
$this->headers[] = [$header, $replace];
|
$this->headers[] = [$header, $replace];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +409,6 @@ class Output
|
||||||
* @param string $mimeType Extension of the file we're outputting
|
* @param string $mimeType Extension of the file we're outputting
|
||||||
* @param string $charset Character set (default: NULL)
|
* @param string $charset Character set (default: NULL)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function setContentType(string $mimeType, $charset = null)
|
public function setContentType(string $mimeType, $charset = null)
|
||||||
{
|
{
|
||||||
if (strpos($mimeType, '/') === false)
|
if (strpos($mimeType, '/') === false)
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FuzeWorks WebComponent.
|
||||||
|
*
|
||||||
|
* The FuzeWorks PHP FrameWork
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013-2019 TechFuze
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author TechFuze
|
||||||
|
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||||
|
* @license https://opensource.org/licenses/MIT MIT License
|
||||||
|
*
|
||||||
|
* @link http://techfuze.net/fuzeworks
|
||||||
|
* @since Version 1.2.0
|
||||||
|
*
|
||||||
|
* @version Version 1.2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FuzeWorks;
|
||||||
|
|
||||||
|
use FuzeWorks\Event\ResourceServeEvent;
|
||||||
|
use FuzeWorks\Exception\WebException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FuzeWorks' handler for static resources.
|
||||||
|
*
|
||||||
|
* Objects in FuzeWorks can register a folder with static resources, which shall be served if requested by clients.
|
||||||
|
* This system should be avoided for high-performance applications. It is recommended to make special configurations in the web server
|
||||||
|
* in those kinds of cases.
|
||||||
|
*/
|
||||||
|
class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private $resources = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Output
|
||||||
|
*/
|
||||||
|
private $output;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->output = Factory::getInstance()->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resourceExists(array $resourceUrlSegments): bool
|
||||||
|
{
|
||||||
|
// First find the resource
|
||||||
|
$file = $this->findResource($resourceUrlSegments);
|
||||||
|
|
||||||
|
// If not found, return false;
|
||||||
|
if (is_null($file))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If found, simply return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serves a static file if found.
|
||||||
|
*
|
||||||
|
* @param array $resourceUrlSegments
|
||||||
|
* @return bool
|
||||||
|
* @throws WebException
|
||||||
|
*
|
||||||
|
* @todo Bypass the Output system and use the readFile() method.
|
||||||
|
* @todo Run as FuzeWorks pre-code, before creating the container
|
||||||
|
*/
|
||||||
|
public function serveResource(array $resourceUrlSegments): bool
|
||||||
|
{
|
||||||
|
// First find the resource
|
||||||
|
$file = $this->findResource($resourceUrlSegments);
|
||||||
|
|
||||||
|
// If not found return false
|
||||||
|
if (is_null($file))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If a file is found, fire a serveResourceEvent
|
||||||
|
/** @var ResourceServeEvent $event */
|
||||||
|
try {
|
||||||
|
$event = Events::fireEvent('resourceServeEvent', $file['resourceName'], $file['segments'], $file['file']);
|
||||||
|
} catch (Exception\EventException $e) {
|
||||||
|
throw new WebException("Could not serve resource. resourceServeEvent threw exception: '" . $e->getMessage() . "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If cancelled, don't serve
|
||||||
|
if ($event->isCancelled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Log the resource serving
|
||||||
|
Logger::log("Serving static resource '/" . $file['resourceName'] . '/' . implode('/', $file['segments']) . "'");
|
||||||
|
|
||||||
|
// Serve file in accordance with event
|
||||||
|
$fileExtension = pathinfo($event->resourceFilePath, PATHINFO_EXTENSION);
|
||||||
|
$this->output->setContentType($fileExtension);
|
||||||
|
$this->output->setOutput(file_get_contents($event->resourceFilePath));
|
||||||
|
#readfile($event->resourceFilePath);
|
||||||
|
|
||||||
|
// And return true at the end
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findResource(array $resourceUrlSegments): ?array
|
||||||
|
{
|
||||||
|
// If too few segments provided, don't even bother
|
||||||
|
if (count($resourceUrlSegments) < 2)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// First segment should be the resourceName, check if it exists
|
||||||
|
$resourceName = urldecode($resourceUrlSegments[1]);
|
||||||
|
if (!isset($this->resources[$resourceName]))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// If resource is found, generate file path
|
||||||
|
$resourceUrlSegmentsBck = $resourceUrlSegments;
|
||||||
|
array_shift($resourceUrlSegments);
|
||||||
|
$file = $this->resources[$resourceName] . DS . implode(DS, $resourceUrlSegments);
|
||||||
|
|
||||||
|
// Test if file exists, if it does, return the string
|
||||||
|
if (file_exists($file) && is_file($file))
|
||||||
|
return ['file' => $file, 'resourceName' => $resourceName, 'segments' => $resourceUrlSegments];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a resource which can be served statically.
|
||||||
|
*
|
||||||
|
* The resourceName will be the directory under which the files shall be served on the web server.
|
||||||
|
* The filePath is where FuzeWorks should look for the files.
|
||||||
|
*
|
||||||
|
* @param string $resourceName
|
||||||
|
* @param string $filePath
|
||||||
|
* @throws WebException
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function registerResources(string $resourceName, string $filePath): bool
|
||||||
|
{
|
||||||
|
// First check if the resource already exists
|
||||||
|
$resourceName = urldecode($resourceName);
|
||||||
|
if (isset($this->resources[$resourceName]))
|
||||||
|
throw new WebException("Could not register resources. Resources with same name already exists.");
|
||||||
|
|
||||||
|
// Also check if the file path exists and is a directory
|
||||||
|
if (!file_exists($filePath) && !is_dir($filePath))
|
||||||
|
throw new WebException("Could not register resources. Provided filePath does not exist.");
|
||||||
|
|
||||||
|
// Add the resource
|
||||||
|
$this->resources[$resourceName] = $filePath;
|
||||||
|
|
||||||
|
// Log the registration
|
||||||
|
Logger::log("Adding static resources on: '/" . $resourceName . "'");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
use FuzeWorks\ConfigORM\ConfigORM;
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
use FuzeWorks\Exception\{ConfigException, SecurityException, Exception};
|
use FuzeWorks\Exception\{ConfigException, CSRFException, Exception};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security Class
|
* Security Class
|
||||||
|
@ -45,6 +45,7 @@ use FuzeWorks\Exception\{ConfigException, SecurityException, Exception};
|
||||||
* @author EllisLab Dev Team
|
* @author EllisLab Dev Team
|
||||||
* @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
|
* @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
|
||||||
* @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
|
* @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
|
||||||
|
* @todo Complete rewrite
|
||||||
*/
|
*/
|
||||||
class Security {
|
class Security {
|
||||||
|
|
||||||
|
@ -168,6 +169,13 @@ class Security {
|
||||||
*/
|
*/
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input. A dependency for this class
|
||||||
|
*
|
||||||
|
* @var Input
|
||||||
|
*/
|
||||||
|
private $input;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class constructor
|
* Class constructor
|
||||||
*
|
*
|
||||||
|
@ -177,6 +185,7 @@ class Security {
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
$this->config = Factory::getInstance()->config->get('security');
|
$this->config = Factory::getInstance()->config->get('security');
|
||||||
|
$this->input = Factory::getInstance()->input;
|
||||||
|
|
||||||
// Is CSRF protection enabled?
|
// Is CSRF protection enabled?
|
||||||
if ($this->config->csrf_protection)
|
if ($this->config->csrf_protection)
|
||||||
|
@ -200,7 +209,7 @@ class Security {
|
||||||
$this->_csrf_set_hash();
|
$this->_csrf_set_hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->charset = strtoupper(Factory::getInstance()->config->get('web')->charset);
|
$this->charset = strtoupper(Factory::getInstance()->config->get('web')->get('charset'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
@ -209,14 +218,13 @@ class Security {
|
||||||
* CSRF Verify
|
* CSRF Verify
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
|
* @throws CSRFException
|
||||||
*/
|
*/
|
||||||
public function csrf_verify(): self
|
public function csrf_verify(): self
|
||||||
{
|
{
|
||||||
// If it's not a POST request we will set the CSRF cookie
|
// If it's not a POST request we will set the CSRF cookie
|
||||||
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
|
if (strtoupper($this->input->server('REQUEST_METHOD')) !== 'POST')
|
||||||
{
|
|
||||||
return $this->csrf_set_cookie();
|
return $this->csrf_set_cookie();
|
||||||
}
|
|
||||||
|
|
||||||
// Check if URI has been whitelisted from CSRF checks
|
// Check if URI has been whitelisted from CSRF checks
|
||||||
if ($exclude_uris = $this->config->csrf_exclude_uris)
|
if ($exclude_uris = $this->config->csrf_exclude_uris)
|
||||||
|
@ -231,22 +239,10 @@ class Security {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the tokens exist in both the _POST and _COOKIE arrays?
|
// Do the tokens exist in both the _POST and _COOKIE arrays?
|
||||||
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
|
$token = $this->input->post($this->_csrf_token_name);
|
||||||
OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
|
$cookie = $this->input->cookie($this->_csrf_cookie_name);
|
||||||
{
|
if ($token == null || $cookie == null || $token !== $cookie)
|
||||||
$this->csrf_show_error();
|
$this->csrf_show_error();
|
||||||
}
|
|
||||||
|
|
||||||
// We kill this since we're done and we don't want to polute the _POST array
|
|
||||||
unset($_POST[$this->_csrf_token_name]);
|
|
||||||
|
|
||||||
// Regenerate on every submission?
|
|
||||||
if ($this->config->csrf_regenerate)
|
|
||||||
{
|
|
||||||
// Nothing should last forever
|
|
||||||
unset($_COOKIE[$this->_csrf_cookie_name]);
|
|
||||||
$this->_csrf_hash = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_csrf_set_hash();
|
$this->_csrf_set_hash();
|
||||||
$this->csrf_set_cookie();
|
$this->csrf_set_cookie();
|
||||||
|
@ -257,6 +253,22 @@ class Security {
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSRF Regenerate
|
||||||
|
*
|
||||||
|
* @throws ConfigException
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function csrf_regenerate()
|
||||||
|
{
|
||||||
|
Logger::log("CSRF Hash is being regenerated...");
|
||||||
|
$this->_csrf_hash = null;
|
||||||
|
$this->_csrf_set_hash();
|
||||||
|
$this->csrf_set_cookie();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSRF Set Cookie
|
* CSRF Set Cookie
|
||||||
*
|
*
|
||||||
|
@ -266,24 +278,25 @@ class Security {
|
||||||
public function csrf_set_cookie()
|
public function csrf_set_cookie()
|
||||||
{
|
{
|
||||||
$expire = time() + $this->_csrf_expire;
|
$expire = time() + $this->_csrf_expire;
|
||||||
$cfg = Factory::getInstance()->config->get('main');
|
$cfg = Factory::getInstance()->config->get('security');
|
||||||
$secure_cookie = (bool) $cfg->cookie_secure;
|
$secure_cookie = (bool) $cfg->csrf_cookie_secure;
|
||||||
|
|
||||||
if ($secure_cookie && ! Core::isHttps())
|
if ($secure_cookie && !$this->input->isHttps())
|
||||||
{
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
|
||||||
|
|
||||||
setcookie(
|
setcookie(
|
||||||
$this->_csrf_cookie_name,
|
$this->_csrf_cookie_name,
|
||||||
$this->_csrf_hash,
|
$this->_csrf_hash,
|
||||||
$expire,
|
$expire,
|
||||||
$cfg->cookie_path,
|
$cfg->csrf_cookie_path,
|
||||||
$cfg->cookie_domain,
|
$cfg->csrf_cookie_domain,
|
||||||
$secure_cookie,
|
$secure_cookie,
|
||||||
$cfg->cookie_httponly
|
$cfg->csrf_cookie_httponly
|
||||||
);
|
);
|
||||||
|
|
||||||
Logger::log('CSRF cookie sent');
|
Logger::log('CSRF cookie sent');
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
@ -292,11 +305,11 @@ class Security {
|
||||||
* Show CSRF Error
|
* Show CSRF Error
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @throws SecurityException
|
* @throws CSRFException
|
||||||
*/
|
*/
|
||||||
public function csrf_show_error()
|
public function csrf_show_error()
|
||||||
{
|
{
|
||||||
throw new SecurityException('The action you have requested is not allowed.', 1);
|
throw new CSRFException('This action resulted in a Cross Site Reference Forgery warning. Request will be blocked...', 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
@ -727,57 +740,6 @@ class Security {
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize Filename
|
|
||||||
*
|
|
||||||
* @param string $str Input file name
|
|
||||||
* @param bool $relative_path Whether to preserve paths
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sanitize_filename($str, $relative_path = FALSE): string
|
|
||||||
{
|
|
||||||
$bad = $this->filename_bad_chars;
|
|
||||||
|
|
||||||
if ( ! $relative_path)
|
|
||||||
{
|
|
||||||
$bad[] = './';
|
|
||||||
$bad[] = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
$str = UTF8::removeInvisibleCharacters($str, FALSE);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
$old = $str;
|
|
||||||
$str = str_replace($bad, '', $str);
|
|
||||||
}
|
|
||||||
while ($old !== $str);
|
|
||||||
|
|
||||||
return stripslashes($str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip Image Tags
|
|
||||||
*
|
|
||||||
* @param string $str
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function strip_image_tags($str): string
|
|
||||||
{
|
|
||||||
return preg_replace(
|
|
||||||
array(
|
|
||||||
'#<img[\s/]+.*?src\s*=\s*(["\'])([^\\1]+?)\\1.*?\>#i',
|
|
||||||
'#<img[\s/]+.*?src\s*=\s*?(([^\s"\'=<>`]+)).*?\>#i'
|
|
||||||
),
|
|
||||||
'\\2',
|
|
||||||
$str
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,17 +60,15 @@ class URI
|
||||||
private $input;
|
private $input;
|
||||||
|
|
||||||
protected $baseUri;
|
protected $baseUri;
|
||||||
protected $uriString;
|
protected $uriString = '';
|
||||||
protected $segments;
|
protected $segments = [];
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
$this->input = Factory::getInstance()->input;
|
$this->input = Factory::getInstance()->input;
|
||||||
$this->config = Factory::getInstance()->config->getConfig('web');
|
$this->config = Factory::getInstance()->config->getConfig('web');
|
||||||
if (WebComponent::$willHandleRequest)
|
if (WebComponent::$willHandleRequest)
|
||||||
{
|
|
||||||
$this->determineUri();
|
$this->determineUri();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function determineUri()
|
public function determineUri()
|
||||||
|
@ -86,7 +84,7 @@ class URI
|
||||||
|
|
||||||
$scriptName = $this->input->server('SCRIPT_NAME');
|
$scriptName = $this->input->server('SCRIPT_NAME');
|
||||||
$scriptFilename = $this->input->server('SCRIPT_FILENAME');
|
$scriptFilename = $this->input->server('SCRIPT_FILENAME');
|
||||||
$baseUrl = ($this->isHttps() ? 'https' : 'http') .
|
$baseUrl = ($this->input->isHttps() ? 'https' : 'http') .
|
||||||
"://" . $serverAddr .
|
"://" . $serverAddr .
|
||||||
substr($scriptName, 0, strpos($scriptName, basename($scriptFilename)));
|
substr($scriptName, 0, strpos($scriptName, basename($scriptFilename)));
|
||||||
}
|
}
|
||||||
|
@ -224,31 +222,4 @@ class URI
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Is HTTPS?
|
|
||||||
*
|
|
||||||
* Determines if the application is accessed via an encrypted
|
|
||||||
* (HTTPS) connection.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function isHttps(): bool
|
|
||||||
{
|
|
||||||
$https = $this->input->server('HTTPS');
|
|
||||||
if (!is_null($https) && strtolower($https) !== 'off')
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
elseif (!is_null($this->input->server('HTTP_X_FORWARDED_PROTO')) && $this->input->server('HTTP_X_FORWARDED_PROTO') === 'https')
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
elseif ( ! is_null($this->input->server('HTTP_FRONT_END_HTTPS')) && strtolower($this->input->server('HTTP_FRONT_END_HTTPS')) !== 'off')
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,14 +36,24 @@
|
||||||
|
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
|
|
||||||
|
use FuzeWorks\Event\HaltExecutionEvent;
|
||||||
|
use FuzeWorks\Event\LayoutLoadEvent;
|
||||||
|
use FuzeWorks\Event\RouterCallViewEvent;
|
||||||
|
use FuzeWorks\Event\RouterLoadViewAndControllerEvent;
|
||||||
|
use FuzeWorks\Event\RouteWebRequestEvent;
|
||||||
|
use FuzeWorks\Exception\ConfigException;
|
||||||
use FuzeWorks\Exception\EventException;
|
use FuzeWorks\Exception\EventException;
|
||||||
use FuzeWorks\Exception\Exception;
|
use FuzeWorks\Exception\Exception;
|
||||||
|
use FuzeWorks\Exception\FactoryException;
|
||||||
|
use FuzeWorks\Exception\HaltException;
|
||||||
use FuzeWorks\Exception\NotFoundException;
|
use FuzeWorks\Exception\NotFoundException;
|
||||||
|
use FuzeWorks\Exception\OutputException;
|
||||||
|
use FuzeWorks\Exception\RouterException;
|
||||||
|
use FuzeWorks\Exception\SecurityException;
|
||||||
use FuzeWorks\Exception\WebException;
|
use FuzeWorks\Exception\WebException;
|
||||||
|
|
||||||
class WebComponent implements iComponent
|
class WebComponent implements iComponent
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether WebComponent is configured to handle a web request
|
* Whether WebComponent is configured to handle a web request
|
||||||
*
|
*
|
||||||
|
@ -64,13 +74,10 @@ class WebComponent implements iComponent
|
||||||
'input' => '\FuzeWorks\Input',
|
'input' => '\FuzeWorks\Input',
|
||||||
'output' => '\FuzeWorks\Output',
|
'output' => '\FuzeWorks\Output',
|
||||||
'uri' => '\FuzeWorks\URI',
|
'uri' => '\FuzeWorks\URI',
|
||||||
|
'resources' => '\FuzeWorks\Resources'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Configurator $configurator
|
|
||||||
* @todo WebComponent will not always be running when added to FuzeWorks, move this into a separate method
|
|
||||||
*/
|
|
||||||
public function onAddComponent(Configurator $configurator)
|
public function onAddComponent(Configurator $configurator)
|
||||||
{
|
{
|
||||||
// Add dependencies
|
// Add dependencies
|
||||||
|
@ -95,60 +102,133 @@ class WebComponent implements iComponent
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On initializing, Initialize UTF8 first, since it's a dependency for other componentClasses
|
||||||
|
*/
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
// First init UTF8
|
// First init UTF8
|
||||||
UTF8::init();
|
UTF8::init();
|
||||||
|
|
||||||
|
// Register some base events
|
||||||
|
Events::addListener([$this, 'layoutLoadEventListener'], 'layoutLoadEvent', Priority::NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the WebComponent to prepare for handling requests
|
||||||
|
*/
|
||||||
public function enableComponent()
|
public function enableComponent()
|
||||||
{
|
{
|
||||||
self::$willHandleRequest = true;
|
self::$willHandleRequest = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the WebComponent so it won't prepare for handling requests
|
||||||
|
*/
|
||||||
public function disableComponent()
|
public function disableComponent()
|
||||||
{
|
{
|
||||||
self::$willHandleRequest = false;
|
self::$willHandleRequest = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handle a Web request.
|
||||||
|
*
|
||||||
|
* Retrieves URI string, routes this URI using the provided routes,
|
||||||
|
* appends output and adds listener to view output on shutdown.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws OutputException
|
||||||
|
* @throws RouterException
|
||||||
* @throws WebException
|
* @throws WebException
|
||||||
|
* @throws EventException
|
||||||
|
* @throws FactoryException
|
||||||
*/
|
*/
|
||||||
public function routeWebRequest(): bool
|
public function routeWebRequest(): bool
|
||||||
{
|
{
|
||||||
if (!self::$willHandleRequest)
|
if (!self::$willHandleRequest)
|
||||||
throw new WebException("Could not route web request. WebComponent is not configured to handle requests");
|
throw new WebException("Could not route web request. WebComponent is not configured to handle requests");
|
||||||
|
|
||||||
// Set the output to display when shutting down
|
|
||||||
try {
|
try {
|
||||||
Events::addListener(function () {
|
// Set the output to display when shutting down
|
||||||
|
Events::addListener(function ($event) {
|
||||||
/** @var Output $output */
|
/** @var Output $output */
|
||||||
Logger::logInfo("Parsing output...");
|
Logger::logInfo("Parsing output...");
|
||||||
$output = Factory::getInstance()->output;
|
$output = Factory::getInstance()->output;
|
||||||
$output->display();
|
$output->display();
|
||||||
|
return $event;
|
||||||
}, 'coreShutdownEvent', Priority::NORMAL);
|
}, 'coreShutdownEvent', Priority::NORMAL);
|
||||||
|
|
||||||
|
// Add HTTP method prefix to requests to views
|
||||||
|
Events::addListener(function($event){
|
||||||
|
/** @var Input $input */
|
||||||
|
/** @var RouterLoadViewAndControllerEvent $event */
|
||||||
|
$input = Factory::getInstance('input');
|
||||||
|
$methods = $event->viewMethods[Priority::NORMAL];
|
||||||
|
foreach ($methods as $method)
|
||||||
|
$event->addMethod(strtolower($input->method()) . '_' . $method);
|
||||||
|
return $event;
|
||||||
|
}, 'routerLoadViewAndControllerEvent', Priority::NORMAL);
|
||||||
|
|
||||||
|
// Create an error 500 page when a haltEvent is fired
|
||||||
|
Events::addListener([$this, 'haltEventListener'], 'haltExecutionEvent', Priority::NORMAL);
|
||||||
} catch (EventException $e) {
|
} catch (EventException $e) {
|
||||||
throw new WebException("Could not route web request. coreShutdownEvent threw EventException: '".$e->getMessage()."'");
|
throw new WebException("Could not route web request. coreShutdownEvent threw EventException: '".$e->getMessage()."'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the X-Powered-By header, since it's a security risk
|
||||||
|
header_remove("X-Powered-By");
|
||||||
|
|
||||||
/** @var Router $router */
|
/** @var Router $router */
|
||||||
$router = Factory::getInstance()->router;
|
/** @var URI $uri */
|
||||||
|
|
||||||
/** @var URI $uriObject */
|
|
||||||
$uriObject = Factory::getInstance()->uri;
|
|
||||||
$uri = $uriObject->uriString();
|
|
||||||
|
|
||||||
/** @var Output $output */
|
/** @var Output $output */
|
||||||
$output = Factory::getInstance()->output;
|
/** @var Security $security */
|
||||||
|
/** @var Resources $resources */
|
||||||
|
$router = Factory::getInstance('router');
|
||||||
|
$uri = Factory::getInstance('uri');
|
||||||
|
$output = Factory::getInstance('output');
|
||||||
|
$security = Factory::getInstance('security');
|
||||||
|
$resources = Factory::getInstance('resources');
|
||||||
|
|
||||||
|
// And start logging the request
|
||||||
|
Logger::newLevel("Routing web request...");
|
||||||
|
|
||||||
|
// First check if a cached page is available
|
||||||
|
$uriString = $uri->uriString();
|
||||||
|
if ($output->getCache($uriString))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Send webRequestEvent, if no cache is found
|
||||||
|
/** @var RouteWebRequestEvent $event */
|
||||||
|
$event = Events::fireEvent('routeWebRequestEvent', $uriString);
|
||||||
|
if ($event->isCancelled())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Attempt to load a static resource
|
||||||
|
if ($resources->serveResource($uri->segmentArray()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// First test for Cross Site Request Forgery
|
||||||
|
try {
|
||||||
|
$security->csrf_verify();
|
||||||
|
} catch (SecurityException $exception) {
|
||||||
|
// If a SecurityException is thrown, first log it
|
||||||
|
Logger::logWarning("SecurityException thrown. Registering listener to verify handler in View");
|
||||||
|
|
||||||
|
// Register a listener
|
||||||
|
Events::addListener([$this, 'callViewEventListener'], 'routerCallViewEvent', Priority::HIGHEST, $exception);
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to load the requested page
|
// Attempt to load the requested page
|
||||||
try {
|
try {
|
||||||
$viewOutput = $router->route($uri);
|
$viewOutput = $router->route($event->uriString);
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
Logger::logWarning("Requested page not found. Requesting Error/error404 View");
|
Logger::logWarning("Requested page not found. Requesting Error/error404 View");
|
||||||
$output->setStatusHeader(404);
|
$output->setStatusHeader(404);
|
||||||
|
|
||||||
// Request 404 page=
|
// Remove listener so that error pages won't be intercepted
|
||||||
|
Events::removeListener([$this, 'callViewEventListener'], 'routerCallViewEvent',Priority::HIGHEST);
|
||||||
|
|
||||||
|
// Request 404 page
|
||||||
try {
|
try {
|
||||||
$viewOutput = $router->route('Error/error404');
|
$viewOutput = $router->route('Error/error404');
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
|
@ -158,14 +238,21 @@ class WebComponent implements iComponent
|
||||||
Logger::exceptionHandler($e, false);
|
Logger::exceptionHandler($e, false);
|
||||||
$viewOutput = 'ERROR 404. Page was not found.';
|
$viewOutput = 'ERROR 404. Page was not found.';
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (HaltException $e) {
|
||||||
Logger::exceptionHandler($e, false);
|
Logger::logWarning("Requested page was denied. Requesting Error/error403 View.");
|
||||||
$output->setStatusHeader(500);
|
$output->setStatusHeader(403);
|
||||||
|
|
||||||
|
// Remove listener so that error pages won't be intercepted
|
||||||
|
Events::removeListener([$this, 'callViewEventListener'], 'routerCallViewEvent',Priority::HIGHEST);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$viewOutput = $router->route('Error/error500');
|
$viewOutput = $router->route('Error/error403');
|
||||||
} catch (Exception $error500Exception) {
|
} catch (NotFoundException $e) {
|
||||||
Logger::exceptionHandler($error500Exception, false);
|
// If still resulting in an error, do something else
|
||||||
$viewOutput = 'ERROR 500. Page could not be loaded.';
|
$viewOutput = 'ERROR 403. Forbidden.';
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Logger::exceptionHandler($e, false);
|
||||||
|
$viewOutput = 'ERROR 403. Forbidden.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +260,99 @@ class WebComponent implements iComponent
|
||||||
if (!empty($viewOutput))
|
if (!empty($viewOutput))
|
||||||
$output->appendOutput($viewOutput);
|
$output->appendOutput($viewOutput);
|
||||||
|
|
||||||
|
Logger::stopLevel();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for routerCallViewEvent
|
||||||
|
*
|
||||||
|
* Fired when a SecurityException is thrown. Verifies if a securityExceptionHandler() method exists.
|
||||||
|
* If not, the calling of the view is cancelled. If yes, the calling of the view depends on the
|
||||||
|
* result of the method
|
||||||
|
*
|
||||||
|
* @param RouterCallViewEvent $event
|
||||||
|
* @param SecurityException $exception
|
||||||
|
*/
|
||||||
|
public function callViewEventListener(RouterCallViewEvent $event, SecurityException $exception)
|
||||||
|
{
|
||||||
|
/** @var RouterCallViewEvent $event */
|
||||||
|
// If the securityExceptionHandler method exists, cancel based on that methods output
|
||||||
|
if (method_exists($event->view, 'securityExceptionHandler'))
|
||||||
|
$event->setCancelled(!$event->view->securityExceptionHandler($exception));
|
||||||
|
|
||||||
|
// If not, cancel it immediately
|
||||||
|
else
|
||||||
|
$event->setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for haltExecutionEvent
|
||||||
|
*
|
||||||
|
* Fired when FuzeWorks halts it's execution. Loads an error 500 page.
|
||||||
|
*
|
||||||
|
* @param $event
|
||||||
|
* @throws EventException
|
||||||
|
* @throws FactoryException
|
||||||
|
* @TODO remove FuzeWorks\Layout dependency
|
||||||
|
*/
|
||||||
|
public function haltEventListener(HaltExecutionEvent $event)
|
||||||
|
{
|
||||||
|
// Dependencies
|
||||||
|
/** @var Output $output */
|
||||||
|
/** @var Router $router */
|
||||||
|
/** @var Event $event */
|
||||||
|
/** @var Layout $layout */
|
||||||
|
$output = Factory::getInstance()->output;
|
||||||
|
$router = Factory::getInstance()->router;
|
||||||
|
$layout = Factory::getInstance()->layouts;
|
||||||
|
|
||||||
|
// Cancel event
|
||||||
|
$event->setCancelled(true);
|
||||||
|
|
||||||
|
// Reset the layout engine
|
||||||
|
$layout->reset();
|
||||||
|
|
||||||
|
// Remove listener so that error pages won't be intercepted
|
||||||
|
Events::removeListener([$this, 'callViewEventListener'], 'routerCallViewEvent',Priority::HIGHEST);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// And handle consequences
|
||||||
|
Logger::logError("Execution halted. Providing error 500 page.");
|
||||||
|
$output->setStatusHeader(500);
|
||||||
|
$viewOutput = $router->route('Error/error500');
|
||||||
|
} catch (Exception $error500Exception) {
|
||||||
|
Logger::exceptionHandler($error500Exception, false);
|
||||||
|
$viewOutput = 'ERROR 500. Page could not be loaded.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally append output and shutdown
|
||||||
|
$output->appendOutput($viewOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for layoutLoadEvent
|
||||||
|
*
|
||||||
|
* Assigns variables from the WebComponent to Layout engines.
|
||||||
|
*
|
||||||
|
* @param LayoutLoadEvent $event
|
||||||
|
* @throws ConfigException
|
||||||
|
* @throws FactoryException
|
||||||
|
*/
|
||||||
|
public function layoutLoadEventListener($event)
|
||||||
|
{
|
||||||
|
// Dependencies
|
||||||
|
/** @var Security $security */
|
||||||
|
/** @var Config $config */
|
||||||
|
$security = Factory::getInstance()->security;
|
||||||
|
$config = Factory::getInstance()->config;
|
||||||
|
|
||||||
|
/** @var LayoutLoadEvent $event */
|
||||||
|
$event->assign('csrfHash', $security->get_csrf_hash());
|
||||||
|
$event->assign('csrfTokenName', $security->get_csrf_token_name());
|
||||||
|
$event->assign('siteURL', $config->getConfig('web')->get('base_url'));
|
||||||
|
$event->assign('serverName', $config->getConfig('web')->get('serverName'));
|
||||||
|
|
||||||
|
Logger::logInfo("Assigned variables to TemplateEngine from WebComponent");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -36,33 +36,6 @@
|
||||||
|
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
|
|
||||||
class WebController extends Controller
|
abstract class WebController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Input
|
|
||||||
*/
|
|
||||||
protected $input;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Output
|
|
||||||
*/
|
|
||||||
protected $output;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var URI
|
|
||||||
*/
|
|
||||||
protected $uri;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebView constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->input = Factory::getInstance()->input;
|
|
||||||
$this->output = Factory::getInstance()->output;
|
|
||||||
$this->uri = Factory::getInstance()->uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,31 +36,6 @@
|
||||||
|
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
|
|
||||||
class WebModel extends Model
|
abstract class WebModel extends Model
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Input
|
|
||||||
*/
|
|
||||||
protected $input;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Output
|
|
||||||
*/
|
|
||||||
protected $output;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var URI
|
|
||||||
*/
|
|
||||||
protected $uri;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebView constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->input = Factory::getInstance()->input;
|
|
||||||
$this->output = Factory::getInstance()->output;
|
|
||||||
$this->uri = Factory::getInstance()->uri;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
namespace FuzeWorks;
|
namespace FuzeWorks;
|
||||||
|
|
||||||
class WebView extends View
|
abstract class WebView extends View
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Input
|
* @var Input
|
||||||
|
@ -53,6 +53,11 @@ class WebView extends View
|
||||||
*/
|
*/
|
||||||
protected $uri;
|
protected $uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Security
|
||||||
|
*/
|
||||||
|
protected $security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebView constructor.
|
* WebView constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -62,5 +67,6 @@ class WebView extends View
|
||||||
$this->input = Factory::getInstance()->input;
|
$this->input = Factory::getInstance()->input;
|
||||||
$this->output = Factory::getInstance()->output;
|
$this->output = Factory::getInstance()->output;
|
||||||
$this->uri = Factory::getInstance()->uri;
|
$this->uri = Factory::getInstance()->uri;
|
||||||
|
$this->security = Factory::getInstance()->security;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue