diff --git a/modules/core/docs/authproc_php.txt b/modules/core/docs/authproc_php.txt index 7fc4a0d49efd28cd9fd3a657bcfcc9ebf2fc69d2..9e0b7bc7153a5dddd29e1b3835a48855b9205f4b 100644 --- a/modules/core/docs/authproc_php.txt +++ b/modules/core/docs/authproc_php.txt @@ -10,10 +10,13 @@ Parameters : This is the name of the filter. It must be `'core:PHP'`. +`function` +: The PHP function that should be run, defined as an anonymous function with one parameter called `$attributes`. + This is an associative array with the user's attributes, and can be modified to add or remove them. + `code` -: The PHP code that should be run. - This PHP code will have one variable available - `$attributes`. - This is an associative array of attributes, and can be modified to add or remove attributes. +: **Deprecated** + If you are using this option, please migrate your code to an anonymous function defined in the `function` option. Examples -------- @@ -22,15 +25,15 @@ Add the `mail` attribute based on the user's `uid` attribute: 10 => array( 'class' => 'core:PHP', - 'code' => ' - if (empty($attributes["uid"])) { - throw new Exception("Missing uid attribute."); + 'function' => function (&$attributes) { + if (empty($attributes['uid'])) { + throw new Exception('Missing uid attribute.'); } - $uid = $attributes["uid"][0]; - $mail = $uid . "@example.net"; - $attributes["mail"] = array($mail); - ', + $uid = $attributes['uid'][0]; + $mail = $uid.'@example.net'; + $attributes['mail'] = array($mail); + }, ), @@ -38,9 +41,9 @@ Create a random number variable: 10 => array( 'class' => 'core:PHP', - 'code' => ' - $attributes["random"] = array( + 'code' => function (&$attributes) { + $attributes['random'] = array( (string)rand(), ); - ', + }, ), diff --git a/modules/core/lib/Auth/Process/PHP.php b/modules/core/lib/Auth/Process/PHP.php index cecf989cccb6076ff2ae7323e3ee0485e687f310..e54af285ee4218d5364a962c4ba710c09569adef 100644 --- a/modules/core/lib/Auth/Process/PHP.php +++ b/modules/core/lib/Auth/Process/PHP.php @@ -1,50 +1,70 @@ <?php + /** * Attribute filter for running arbitrary PHP code. * * @package simpleSAMLphp */ -class sspmod_core_Auth_Process_PHP extends SimpleSAML_Auth_ProcessingFilter { - - /** - * The PHP code that should be run. - * - * @var string - */ - private $code; - - - /** - * Initialize this filter, parse configuration - * - * @param array $config Configuration information about this filter. - * @param mixed $reserved For future use. - */ - public function __construct($config, $reserved) { - parent::__construct($config, $reserved); - - assert('is_array($config)'); - - if (!isset($config['code'])) { - throw new SimpleSAML_Error_Exception($this->authId . ': Missing required \'code\' option.'); - } - - $this->code = (string)$config['code']; - } - - - /** - * Apply the PHP code to the attribtes. - * - * @param array &$request The current request - */ - public function process(&$request) { - assert('is_array($request)'); - assert('array_key_exists("Attributes", $request)'); - - $function = create_function('&$attributes', $this->code); - $function($request['Attributes']); - } +class sspmod_core_Auth_Process_PHP extends SimpleSAML_Auth_ProcessingFilter +{ + + /** + * The PHP code that should be run. + * + * @var string + */ + private $code; + + /** + * @var callable + */ + private $function = null; + + + /** + * Initialize this filter, parse configuration + * + * @param array $config Configuration information about this filter. + * @param mixed $reserved For future use. + */ + public function __construct($config, $reserved) + { + parent::__construct($config, $reserved); + + assert('is_array($config)'); + + if (isset($config['function'])) { + $this->function = $config['function']; + } else { // TODO: remove this branch after removing the 'code' option. + if (!isset($config['code'])) { + throw new SimpleSAML_Error_Exception("core:PHP: Neither 'function' nor 'code' options defined."); + } + SimpleSAML_Logger::warning( + "Deprecated 'code' configuration option in PHP authentication processing filter." + ); + $this->code = (string) $config['code']; + } + } + + + /** + * Apply the PHP code to the attributes. + * + * @param array &$request The current request + */ + public function process(&$request) + { + assert('is_array($request)'); + assert('array_key_exists("Attributes", $request)'); + + if ($this->function) { + $function = $this->function; + $function($request['Attributes']); + } else { // TODO: remove this branch after removing the 'code' option. + $function = create_function('&$attributes', $this->code); + $function($request['Attributes']); + } + } } diff --git a/tests/modules/core/lib/Auth/Process/PHPTest.php b/tests/modules/core/lib/Auth/Process/PHPTest.php new file mode 100644 index 0000000000000000000000000000000000000000..66c552377162d2d6bb18a2ecaa29a516134dc7ec --- /dev/null +++ b/tests/modules/core/lib/Auth/Process/PHPTest.php @@ -0,0 +1,103 @@ +<?php + + +/** + * Test for the core:PHP filter. + */ +class Test_Core_Auth_Process_PHP extends PHPUnit_Framework_TestCase +{ + + /** + * Helper function to run the filter with a given configuration. + * + * @param array $config The filter configuration. + * @param array $request The request state. + * + * @return array The state array after processing. + */ + private static function processFilter(array $config, array $request) + { + $filter = new sspmod_core_Auth_Process_PHP($config, null); + @$filter->process($request); + return $request; + } + + + /** + * Test the configuration of the filter. + * + * @expectedException SimpleSAML_Error_Exception + */ + public function testInvalidConfiguration() + { + $config = array(); + new sspmod_core_Auth_Process_PHP($config, null); + } + + + + /** + * Check that defining a function works as expected. + */ + public function testFunctionDefined() + { + $config = array( + 'function' => function (&$attributes) { + $attributes['key'] = 'value'; + }, + ); + $request = array('Attributes' => array()); + $expected = array( + 'Attributes' => array( + 'key' => 'value', + ), + ); + + $this->assertEquals($expected, $this->processFilter($config, $request)); + } + + + /** + * Check that defining the code works as expected. + */ + public function testCodeDefined() + { + $config = array( + 'code' => ' + $attributes["key"] = "value"; + ', + ); + $request = array('Attributes' => array()); + $expected = array( + 'Attributes' => array( + 'key' => 'value', + ), + ); + + $this->assertEquals($expected, $this->processFilter($config, $request)); + } + + + /** + * Check that when both the function and code are defined, only the function is executed. + */ + public function testOptionsPrecedence() + { + $config = array( + 'function' => function (&$attributes) { + $attributes['who'] = 'function'; + }, + 'code' => ' + $attributes["who"] = "code"; + ', + ); + $request = array('Attributes' => array()); + $expected = array( + 'Attributes' => array( + 'who' => 'function', + ), + ); + + $this->assertEquals($expected, $this->processFilter($config, $request)); + } +}