From 2d814a3e9ae79dbebde5705b2e38c16075427229 Mon Sep 17 00:00:00 2001 From: RDS-Team <rds@redi-bw.de> Date: Tue, 10 Mar 2015 13:45:52 -0400 Subject: [PATCH] Added ServerParam permission provider (and tests). --- config/vufind/permissions.ini | 33 ++- module/VuFind/config/module.config.php | 1 + .../Role/PermissionProvider/Factory.php | 12 + .../Role/PermissionProvider/ServerParam.php | 204 ++++++++++++++++ .../PermissionProvider/ServerParamTest.php | 229 ++++++++++++++++++ 5 files changed, 466 insertions(+), 13 deletions(-) create mode 100644 module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php create mode 100644 module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php diff --git a/config/vufind/permissions.ini b/config/vufind/permissions.ini index 734f88ee6f2..87f703e397b 100644 --- a/config/vufind/permissions.ini +++ b/config/vufind/permissions.ini @@ -18,18 +18,25 @@ ; The values associated with these keys will be passed along to the services. ; You can define your own permission providers, or use some of the following: ; -; ipRange - Grant the permission to the single IP adresse or to the range. -; Accepts a single IP adresse or a range with a minus character without -; blanks as seperator. -; ipRegEx - Grant the permission to IP addresses matching the provided regular -; expression(s). Accepts a string or an array; if an array is passed, -; permission will be granted if ANY one of the expressions matches. -; role - Grant the permission automatically to the role or roles specified -; (accepts a string or an array). Note that VuFind uses 'guest' for -; logged-out users and 'loggedin' for all logged-in users. You may -; define additional roles with custom code. -; username - Grant the permission to logged-in users whose usernames match the -; specified value(s). Accepts a string or an array. +; ipRange - Grant the permission to the single IP adresse or to the range. +; Accepts a single IP adresse or a range with a minus character without +; blanks as seperator. +; ipRegEx - Grant the permission to IP addresses matching the provided regular +; expression(s). Accepts a string or an array; if an array is passed, +; permission will be granted if ANY one of the expressions matches. +; role - Grant the permission automatically to the role or roles specified +; (accepts a string or an array). Note that VuFind uses 'guest' for +; logged-out users and 'loggedin' for all logged-in users. You may +; define additional roles with custom code. +; serverParam - Grant the permission if request server params match the given rules. +; Accepts a string or an array; if an array is passed permission will +; be granted if ALL of the rules match. Rules are specified as +; <server param name> [modifier] <value> [<value 2> ... <value n>] +; with optional modifier ~ (match instead of string comparison, values +; are treated as regular expressions), ! (not) or !~ (no match). Only +; one of the values must match (OR). +; username - Grant the permission to logged-in users whose usernames match the +; specified value(s). Accepts a string or an array. ; ; Example configuration (grants the "sample.permission" permission to users named ; admin1 or admin2, or anyone coming from the IP addresses 1.2.3.4 or 1.2.3.5): @@ -52,4 +59,4 @@ ; Default configuration for the EIT module; see EIT.ini for some notes on this. [default.EITModule] role = loggedin -permission = access.EITModule \ No newline at end of file +permission = access.EITModule diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index a5dc14c287a..afd315515cb 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -674,6 +674,7 @@ $config = [ 'factories' => [ 'ipRange' => 'VuFind\Role\PermissionProvider\Factory::getIpRange', 'ipRegEx' => 'VuFind\Role\PermissionProvider\Factory::getIpRegEx', + 'serverParam' => 'VuFind\Role\PermissionProvider\Factory::getServerParam', 'username' => 'VuFind\Role\PermissionProvider\Factory::getUsername', ], 'invokables' => [ diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php index 463820ae950..f6944195a1a 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php @@ -65,6 +65,18 @@ class Factory return new IpRegEx($sm->getServiceLocator()->get('Request')); } + /** + * Factory for ServerParam + * + * @param ServiceManager $sm Service manager. + * + * @return ServerParam + */ + public static function getServerParam(ServiceManager $sm) + { + return new ServerParam($sm->getServiceLocator()->get('Request')); + } + /** * Factory for Username * diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php b/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php new file mode 100644 index 00000000000..615f0751eab --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/ServerParam.php @@ -0,0 +1,204 @@ +<?php +/** + * ServerParam permission provider for VuFind. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2007. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <lienhard@ub.uni-freiburg.de> + * @author Bernd Oberknapp <bo@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://www.vufind.org Main Page + */ +namespace VuFind\Role\PermissionProvider; +use Zend\Http\PhpEnvironment\Request; + +/** + * ServerParam permission provider for VuFind. + * + * @category VuFind2 + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @author Jochen Lienhard <lienhard@ub.uni-freiburg.de> + * @author Bernd Oberknapp <bo@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://www.vufind.org Main Page + */ +class ServerParam implements PermissionProviderInterface, + \Zend\Log\LoggerAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait; + + /** + * Request object + * + * @var Request + */ + protected $request; + + /** + * Aliases for server param names (default: none) + * + * @var array + */ + protected $aliases = []; + + /** + * Delimiter for multi-valued server params (default: none) + * + * @var string + */ + protected $serverParamDelimiter = ''; + + /** + * Escape character for delimiter in server param strings (default: none) + * + * @var string + */ + protected $serverParamEscape = ''; + + /** + * Constructor + * + * @param Request $request Request object + */ + public function __construct(Request $request) + { + $this->request = $request; + } + + /** + * Return an array of roles which may be granted the permission based on + * the options. + * + * @param mixed $options Options provided from configuration. + * + * @return array + */ + public function getPermissions($options) + { + // user only gets the permission if all options match (AND) + foreach ((array)$options as $option) { + $this->debug("getPermissions: option '{$option}'"); + if (!$this->checkServerParam($option)) { + $this->debug("getPermissions: result = false"); + return []; + } + $this->debug("getPermissions: result = true"); + } + return ['loggedin']; + } + + /** + * Check if a server param matches the option. + * + * @param string $option Option + * + * @return boolean true if a server param matches, false if not + */ + protected function checkServerParam($option) + { + // split option on spaces unless escaped with backslash + $optionParts = $this->splitString($option, ' ', '\\'); + if (count($optionParts) < 2) { + $this->logError("configuration option '{$option}' invalid"); + return false; + } + + // first part is the server param name + $serverParamName = array_shift($optionParts); + if (isset($this->aliases[$serverParamName])) { + $serverParamName = $this->aliases[$serverParamName]; + } + + // optional modifier follow server param name + $modifierMatch = in_array($optionParts[0], ['~', '!~']); + $modifierNot = in_array($optionParts[0], ['!', '!~']); + if ($modifierNot || $modifierMatch) { + array_shift($optionParts); + } + + // remaining parts are the templates for checking the server params + $templates = $optionParts; + if (empty($templates)) { + $this->logError("configuration option '{$option}' invalid"); + return false; + } + + // server param values to check + $serverParamString = $this->request->getServer()->get($serverParamName); + if ($serverParamString === false) { + // check fails if server param is missing + return false; + } + $serverParams = $this->splitString( + $serverParamString, $this->serverParamDelimiter, $this->serverParamEscape + ); + + $result = false; + // check for each server param ... + foreach ($serverParams as $serverParam) { + // ... if it matches one of the templates (OR) + foreach ($templates as $template) { + if ($modifierMatch) { + $result |= preg_match('/' . $template . '/', $serverParam); + } else { + $result |= ($template === $serverParam); + } + } + } + if ($modifierNot) { + $result = !$result; + } + + return $result; + } + + /** + * Split string on delimiter unless dequalified with escape + * + * @param string $string String to split + * @param string $delimiter Delimiter character + * @param string $escape Escape character + * + * @return array split string parts + */ + protected function splitString($string, $delimiter, $escape) + { + if ($delimiter === '') { + return [$string]; + } + + if ($delimiter === ' ') { + $pattern = ' +'; + } else { + $pattern = preg_quote($delimiter, '/'); + } + + if ($escape === '') { + $pattern = '(?<!' . preg_quote($escape, '/') . ')' . $pattern; + } + + return str_replace( + $escape . $delimiter, $delimiter, + preg_split('/' . $pattern . '/', $string) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php new file mode 100644 index 00000000000..84b59dbe42c --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/ServerParamTest.php @@ -0,0 +1,229 @@ +<?php +/** + * PermissionProvider ServerParam Test Class + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @author Bernd Oberknapp <bo@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:unit_tests Wiki + */ +namespace VuFindTest\Role\PermissionProvider; +use VuFind\Role\PermissionProvider\ServerParam; + +/** + * PermissionProvider ServerParam Test Class + * + * @category VuFind2 + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @author Bernd Oberknapp <bo@ub.uni-freiburg.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:unit_tests Wiki + */ +class ServerParamTest extends \VuFindTest\Unit\TestCase +{ + /** + * Test single option with matching string + * + * @return void + */ + public function testStringTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + 'testheader testvalue', + ['loggedin'] + ); + } + + /** + * Test option array with matching string + * + * @return void + */ + public function testArrayTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader testvalue'], + ['loggedin'] + ); + } + + /** + * Test multiple options with matching headers + * + * @return void + */ + public function testOptionsAndTrue() + { + $this->checkServerParams( + ['testheader1' => 'testvalue1', 'testheader2' => 'testvalue2'], + ['testheader1 testvalue1', 'testheader2 testvalue2'], + ['loggedin'] + ); + } + + /** + * Test multiple options with no matching header + * + * @return void + */ + public function testOptionsAndFalse() + { + $this->checkServerParams( + ['testheader1' => 'testvalue1'], + ['testheader1 testvalue1', 'testheader2 testvalue2'], + [] + ); + } + + /** + * Test option with multiple values and matching header + * + * @return void + */ + public function testOptionValuesOrTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue1'], + ['testheader testvalue1 testvalue2'], + ['loggedin'] + ); + } + + /** + * Test option with multiple values and no matching header + * + * @return void + */ + public function testOptionValuesOrFalse() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader testvalue1 testvalue2'], + [] + ); + } + + /** + * Test option with regex modifier and matching header + * + * @return void + */ + public function testOptionRegexTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader ~ ^testvalue$'], + ['loggedin'] + ); + } + + /** + * Test option with regex modifier and no matching header + * + * @return void + */ + public function testOptionRegexFalse() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader ~ ^estvalue'], + [] + ); + } + + /** + * Test option with not modifier and matching header + * + * @return void + */ + public function testOptionNotTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader ! testval'], + ['loggedin'] + ); + } + + /** + * Test option with not modifier and no matching header + * + * @return void + */ + public function testOptionNotFalse() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader ! testvalue'], + [] + ); + } + + /** + * Test option with not regex modifier and matching header + * + * @return void + */ + public function testOptionNotRegexTrue() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader !~ testval$'], + ['loggedin'] + ); + } + + /** + * Test option with not regex modifier and no matching header + * + * @return void + */ + public function testOptionNotRegexFalse() + { + $this->checkServerParams( + ['testheader' => 'testvalue'], + ['testheader !~ ^testvalue'], + [] + ); + } + + /** + * Setup request and header objects, run getPermissions and check the result + * + * @param array $headers Request headers + * @param mixed $options options as from configuration + * @param array $expectedResult expected result returned by getPermissions + * + * @return void + */ + protected function checkServerParams($headers, $options, $expectedResult) + { + $request = new \Zend\Http\PhpEnvironment\Request(); + $request->setServer(new \Zend\Stdlib\Parameters($headers)); + $header = new ServerParam($request); + $result = $header->getPermissions($options); + $this->assertEquals($result, $expectedResult); + } +} -- GitLab