Adamw has uploaded a new change for review.
https://gerrit.wikimedia.org/r/51601
Change subject: Experimental hook implementation.
......................................................................
Experimental hook implementation.
Cleans up the entry point, and move the PublicHttp to "examples" to
make it clear that this is not part of the class hierarchy, and
should definitely not be run unmodified as a production solution.
Change-Id: Ie1429cd58a6b7c19d1673ef5d344170ff12979cc
---
M SmashPig/Core/AutoLoader.php
M SmashPig/Core/Configuration.php
M SmashPig/Core/Http/RequestHandler.php
M SmashPig/Core/Listeners/ListenerBase.php
M SmashPig/Core/Listeners/SoapListener.php
A SmashPig/Core/Main.php
A SmashPig/Core/Security/IpWhitelist.php
D SmashPig/PublicHttp/smashpig_http_handler.php
M SmashPig/config_defaults.php
R SmashPig/examples/.htaccess
A SmashPig/examples/http_listener.php
11 files changed, 150 insertions(+), 115 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/PaymentsListeners
refs/changes/01/51601/1
diff --git a/SmashPig/Core/AutoLoader.php b/SmashPig/Core/AutoLoader.php
index 4cd6644..1f44ec8 100644
--- a/SmashPig/Core/AutoLoader.php
+++ b/SmashPig/Core/AutoLoader.php
@@ -60,7 +60,7 @@
\SmashPig\Core\Logging\Logger::debug( 'Loading
additional namespace node.', $namespace );
AutoLoader::getInstance()->addNamespacePath(
$namespace['namespace'],
- $namespace['disk-path']
+ realpath( $namespace['disk-path'] )
);
}
}
@@ -72,7 +72,7 @@
// Load additional namespaces into the autoloader
foreach ( $config->val( 'include-paths' ) as $path ) {
- $path .= PATH_SEPARATOR . $path;
+ $path .= PATH_SEPARATOR . realpath( $path );
}
\SmashPig\Core\Logging\Logger::debug( 'Setting include path to:
', $path );
@@ -102,7 +102,7 @@
throw new SmashPigException( "Namespace $namespace already has a
loader entry." );
}
- $ref[ '@' ] = $path;
+ $ref[ '@' ] = realpath( $path );
}
/**
diff --git a/SmashPig/Core/Configuration.php b/SmashPig/Core/Configuration.php
index 58ece79..87a1590 100644
--- a/SmashPig/Core/Configuration.php
+++ b/SmashPig/Core/Configuration.php
@@ -24,6 +24,7 @@
*
* @return Configuration
*/
+ //FIXME: getCurrent
public static function &getDefaultConfig() {
return Configuration::$defaultObj;
}
diff --git a/SmashPig/Core/Http/RequestHandler.php
b/SmashPig/Core/Http/RequestHandler.php
index d3a610d..d5b29da 100644
--- a/SmashPig/Core/Http/RequestHandler.php
+++ b/SmashPig/Core/Http/RequestHandler.php
@@ -42,9 +42,7 @@
$view = array_shift( $parts );
$action = array_shift( $parts );
- // --- Initialize core services ---
- $config = new Configuration( 'config_defaults.php', 'config.php',
$view, true );
- Logger::init( $config->val( 'logging/root-context' ), $config->val(
'logging/log-level' ), $config );
+ $config = Configuration::getDefaultConfig();
set_error_handler(
'\SmashPig\Core\Http\RequestHandler::lastChanceErrorHandler' );
set_exception_handler(
'\SmashPig\Core\Http\RequestHandler::lastChanceExceptionHandler' );
@@ -55,10 +53,6 @@
static::$response->killResponse( 403, "Action '$action' not
configured. Cannot continue." );
return;
}
-
- // Register fun additional things
- AutoLoader::getInstance()->addConfiguredIncludePaths();
- AutoLoader::getInstance()->addConfiguredNamespaces();
// --- Actually get the endpoint object and start the request ---
$endpointObj = $config->obj( "endpoints/$action" );
diff --git a/SmashPig/Core/Listeners/ListenerBase.php
b/SmashPig/Core/Listeners/ListenerBase.php
index ab681c7..c3e244e 100644
--- a/SmashPig/Core/Listeners/ListenerBase.php
+++ b/SmashPig/Core/Listeners/ListenerBase.php
@@ -24,88 +24,6 @@
public abstract function execute( Request $request, Response $response,
$pathParts );
/**
- * Perform security checks that do not rely on the contents of the
envelope or the message.
- *
- * @throws ListenerSecurityException on unacceptable ingress data. Message
processing should
- * stop and the data logged.
- */
- protected function doIngressSecurity() {
- $this->validateRemoteIp();
- }
-
- /**
- * Determine remote IP address and check validity against an IP whitelist.
Will throw exception
- * on error or invalid IP.
- *
- * TODO: This function only handles IPv4 -- it should also handle v6
- *
- * @throws ListenerConfigException
- * @throws ListenerSecurityException
- */
- protected function validateRemoteIp() {
- // Obtain whitelist
- $whitelist = $this->c->val( 'security/ip-whitelist', true );
-
- if ( empty( $whitelist ) ) {
- Logger::info( 'No IP whitelist specified. Continuing and not
validating remote IP.' );
- return;
- }
-
- // Obtain remote party IP
- $ipHeaderName = $this->c->val( 'security/ip-header-name' );
- if ( $ipHeaderName !== '' ) {
- $headers = getallheaders();
- if ( !$headers || !array_key_exists( $ipHeaderName, $headers ) ) {
- throw new ListenerConfigException(
- "Unexpected platform issue when trying to get remote box's
IP from '{$ipHeaderName}'"
- );
- }
- $remote_ip = $headers[ $ipHeaderName ];
- }
- else {
- $remote_ip = $_SERVER[ 'REMOTE_ADDR' ];
- }
-
- // Validate remote party IP (right now we can only handle IPv4)
- if ( !filter_var( $remote_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
{
- throw new ListenerSecurityException( "Bizarre remote IP address:
{$remote_ip}" );
- }
-
- // Check remote address against the IP whitelist -- the whitelist can
be either individual
- // or CIDR blocks.
- foreach ( (array)$whitelist as $ip ) {
- if ( $remote_ip === $ip ) {
- return;
- } elseif ( count( explode( '/', $ip ) ) === 2 ) {
- // Obtain address, CIDR block, and verify correctness of form
- list( $network_ip, $block ) = explode( '/', $ip );
- if ( !filter_var( $network_ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 ) ||
- !filter_var( $block, FILTER_VALIDATE_INT, array(
'min_range' => 0, 'max_range' => 32 ) )
- ) {
- throw new ListenerConfigException( "Malformed IP address
in whitelist: {$ip}" );
- }
-
- // Obtain raw IP addresses
- $network_long = ip2long( $network_ip );
- $mask_long = ~( pow( 2, ( 32 - $block ) ) - 1 );
- $remote_long = ip2long( $remote_ip );
-
- // Validate in CIDR
- if ( ( $remote_long & $mask_long ) === ( $network_long &
$mask_long ) ) {
- return; // the remote IP address is in this range
- }
- } else {
- throw new ListenerConfigException( "Malformed IP address in
whitelist: {$ip}" );
- }
- }
-
- // we have fallen through everything in the whitelist, throw
- throw new ListenerSecurityException(
- "Received a connection from a bogus IP: {$remote_ip}, agent:
{$_SERVER['HTTP_USER_AGENT']}"
- );
- }
-
- /**
* Message object specific processing --
security/validation/actions/acknowledgement of message
* from envelope.
*
diff --git a/SmashPig/Core/Listeners/SoapListener.php
b/SmashPig/Core/Listeners/SoapListener.php
index d38a69e..22a8438 100644
--- a/SmashPig/Core/Listeners/SoapListener.php
+++ b/SmashPig/Core/Listeners/SoapListener.php
@@ -1,6 +1,7 @@
<?php namespace SmashPig\Core\Listeners;
use SmashPig\Core;
+use SmashPig\Core\Hook;
use SmashPig\Core\Logging\Logger;
use SmashPig\Core\Http\Request;
use SmashPig\Core\Http\Response;
@@ -32,7 +33,8 @@
Logger::info( "Starting processing of listener request from
{$_SERVER['REMOTE_ADDR']}" );
try {
- $this->doIngressSecurity();
+ //XXX or simply 'ingress' ?
+ Hook::run( 'security/ingress', $this );
$soapData = $request->getRawPostData();
Logger::enterContext( 'RawData' );
diff --git a/SmashPig/Core/Main.php b/SmashPig/Core/Main.php
new file mode 100644
index 0000000..4bf4997
--- /dev/null
+++ b/SmashPig/Core/Main.php
@@ -0,0 +1,42 @@
+<?php namespace Smashpig\Core;
+
+class Main {
+ //XXX this is encouraging nefarious paradigms. Consider moving to
initialize() scope.
+ static $singleton;
+
+ /**
+ */
+ static function initialize( $name, $view = 'default' ) {
+ // Assert singleton application entry point.
+ if ( !self::$singleton ) {
+ self::$singleton = $name;
+ } else {
+ $str = <<<EOT
+SmashPig has detected that multiple execution entry points have been used in a
+single session. Execution of the {$name} entry point cannot continue at this
time,
+lock is held by
+EOT;
+ print( $str . " " . self::$singleton );
+ }
+
+ // --- Initialize core services ---
+ require_once( __DIR__ . "/AutoLoader.php" );
+ SmashPig\Core\Autoloader::installSmashPigAutoLoader( __DIR__ . "/../"
);
+
+ $config = new Configuration( 'config_defaults.php', 'config.php',
$view, true );
+
+ //FIXME inconsistent to sometimes pass $config
+ Logger::init( $config->val( 'logging/root-context' ), $config->val(
'logging/log-level' ), $config );
+
+ // Register fun additional things
+ AutoLoader::getInstance()->addConfiguredIncludePaths();
+ AutoLoader::getInstance()->addConfiguredNamespaces();
+
+ foreach ( (array)$config->val( 'modules' ) as $classspec ) {
+ $module = new $classspec();
+ if ( $module instanceof IHookable ) {
+ Hook::registerAll( $module->getHooks() );
+ }
+ }
+ }
+}
diff --git a/SmashPig/Core/Security/IpWhitelist.php
b/SmashPig/Core/Security/IpWhitelist.php
new file mode 100644
index 0000000..ead0548
--- /dev/null
+++ b/SmashPig/Core/Security/IpWhitelist.php
@@ -0,0 +1,86 @@
+<?php namespace SmashPig\Core\Security\IpWhitelist;
+/**
+ * Mixin this module if you maintain an IP whitelist.
+ */
+
+use SmashPig\Core\IHookable;
+
+class IpWhitelist implements IHookable {
+ /**
+ * Determine remote IP address and check validity against an IP whitelist.
Will throw exception
+ * on error or invalid IP.
+ *
+ * TODO: This function only handles IPv4 -- it should also handle v6
+ *
+ * @throws ListenerConfigException
+ * @throws ListenerSecurityException
+ */
+ function validateRemoteIp() {
+ // Obtain whitelist
+ $whitelist = $this->c->val( 'security/ip-whitelist', true );
+
+ if ( empty( $whitelist ) ) {
+ Logger::info( 'No IP whitelist specified. Continuing and not
validating remote IP.' );
+ return;
+ }
+
+ // Obtain remote party IP
+ $ipHeaderName = $this->c->val( 'security/ip-header-name' );
+ if ( $ipHeaderName !== '' ) {
+ $headers = getallheaders();
+ if ( !$headers || !array_key_exists( $ipHeaderName, $headers ) ) {
+ throw new ListenerConfigException(
+ "Unexpected platform issue when trying to get remote box's
IP from '{$ipHeaderName}'"
+ );
+ }
+ $remote_ip = $headers[ $ipHeaderName ];
+ }
+ else {
+ $remote_ip = $_SERVER[ 'REMOTE_ADDR' ];
+ }
+
+ // Validate remote party IP (right now we can only handle IPv4)
+ if ( !filter_var( $remote_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
{
+ throw new ListenerSecurityException( "Bizarre remote IP address:
{$remote_ip}" );
+ }
+
+ // Check remote address against the IP whitelist -- the whitelist can
be either individual
+ // or CIDR blocks.
+ foreach ( (array)$whitelist as $ip ) {
+ if ( $remote_ip === $ip ) {
+ return;
+ } elseif ( count( explode( '/', $ip ) ) === 2 ) {
+ // Obtain address, CIDR block, and verify correctness of form
+ list( $network_ip, $block ) = explode( '/', $ip );
+ if ( !filter_var( $network_ip, FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 ) ||
+ !filter_var( $block, FILTER_VALIDATE_INT, array(
'min_range' => 0, 'max_range' => 32 ) )
+ ) {
+ throw new ListenerConfigException( "Malformed IP address
in whitelist: {$ip}" );
+ }
+
+ // Obtain raw IP addresses
+ $network_long = ip2long( $network_ip );
+ $mask_long = ~( pow( 2, ( 32 - $block ) ) - 1 );
+ $remote_long = ip2long( $remote_ip );
+
+ // Validate in CIDR
+ if ( ( $remote_long & $mask_long ) === ( $network_long &
$mask_long ) ) {
+ return; // the remote IP address is in this range
+ }
+ } else {
+ throw new ListenerConfigException( "Malformed IP address in
whitelist: {$ip}" );
+ }
+ }
+
+ // we have fallen through everything in the whitelist, throw
+ throw new ListenerSecurityException(
+ "Received a connection from a bogus IP: {$remote_ip}, agent:
{$_SERVER['HTTP_USER_AGENT']}"
+ );
+ }
+
+ function getHooks() {
+ return array(
+ 'network/ingress', array( 'IpWhitelist', 'validateRemoteIp' ),
+ );
+ }
+}
diff --git a/SmashPig/PublicHttp/smashpig_http_handler.php
b/SmashPig/PublicHttp/smashpig_http_handler.php
deleted file mode 100644
index 21f010d..0000000
--- a/SmashPig/PublicHttp/smashpig_http_handler.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php namespace SmashPig;
-
-use SmashPig\Core\AutoLoader;
-use SmashPig\Core\Http\RequestHandler;
-
-$file = __FILE__;
-
-if ( !defined( "SMASHPIG_ENTRY_POINT" ) ) {
- define( "SMASHPIG_ENTRY_POINT", $file );
-
- require_once( './Core/AutoLoader.php' );
- AutoLoader::installSmashPigAutoLoader( './' );
-
- RequestHandler::process();
-} else {
- $str = <<<EOT
-SmashPig has detected that multiple execution entry points have been used in a
-single session. Execution of the {$file} entry point cannot continue at this
time.
-EOT;
- print( $str );
-}
-
diff --git a/SmashPig/config_defaults.php b/SmashPig/config_defaults.php
index 2f4ca44..c2ab00e 100644
--- a/SmashPig/config_defaults.php
+++ b/SmashPig/config_defaults.php
@@ -55,6 +55,11 @@
),
),
+
+ 'modules' => array(
+ 'SmashPig\Core\Security\IpWhitelist',
+ ),
+
'security' => array(
'ip-header-name' => '',
'ip-whitelist' => array(),
diff --git a/SmashPig/PublicHttp/.htaccess b/SmashPig/examples/.htaccess
similarity index 100%
rename from SmashPig/PublicHttp/.htaccess
rename to SmashPig/examples/.htaccess
diff --git a/SmashPig/examples/http_listener.php
b/SmashPig/examples/http_listener.php
new file mode 100644
index 0000000..ee17cc9
--- /dev/null
+++ b/SmashPig/examples/http_listener.php
@@ -0,0 +1,9 @@
+<?php
+
+// An absolute path is recommended.
+$smashpig_path = __DIR__ . "/../";
+
+require_once "{$smashpig_path}/Core/Main";
+SmashPig\Core\EntryPoint::initialize( __FILE __ );
+
+RequestHandler::process();
--
To view, visit https://gerrit.wikimedia.org/r/51601
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie1429cd58a6b7c19d1673ef5d344170ff12979cc
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/PaymentsListeners
Gerrit-Branch: master
Gerrit-Owner: Adamw <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits