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

Reply via email to