From a0cc5f7cb33ec76d6506781db2bc2345046324a2 Mon Sep 17 00:00:00 2001
From: Markus Beh <markus.beh@ub.uni-freiburg.de>
Date: Mon, 13 Jun 2016 14:59:25 -0400
Subject: [PATCH] new permission provider: grant permissions based on user
 attributes

---
 config/vufind/permissions.ini                 |  10 +-
 module/VuFind/config/module.config.php        |   1 +
 .../Role/PermissionProvider/Factory.php       |  14 ++
 .../VuFind/Role/PermissionProvider/User.php   | 103 ++++++++++++
 .../Role/PermissionProvider/UserTest.php      | 158 ++++++++++++++++++
 5 files changed, 285 insertions(+), 1 deletion(-)
 create mode 100644 module/VuFind/src/VuFind/Role/PermissionProvider/User.php
 create mode 100644 module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php

diff --git a/config/vufind/permissions.ini b/config/vufind/permissions.ini
index f9d95a31f82..e480e9d1e45 100644
--- a/config/vufind/permissions.ini
+++ b/config/vufind/permissions.ini
@@ -45,6 +45,9 @@
 ;               rule that checks an attribute.
 ; username    - Grant the permission to logged-in users whose usernames match the
 ;               specified value(s). Accepts a string or an array.
+; user        - Grant the permissions to logged in users whose user attribute match
+;               the given regular expression pattern. For valid pattern syntax see
+;               http://php.net/manual/de/reference.pcre.pattern.syntax.php.
 ;
 ; 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):
@@ -57,7 +60,12 @@
 ; ipRange[] = "1.2.3.4"
 ; ipRange[] = "1.2.3.7-1.2.5.254"
 ; permission = sample.permission
-;
+
+; Example configuration (grants the "sample.permission" permission to users 
+; who are from myCollege or who is a studentmajor (.*studentmajor.*):
+; user[] = "college myCollege"
+; user[] = "major .*studentmajor.*"
+
 ; List of permissions that you may wish to configure:
 ;
 ; access.AdminModule - Controls access to the admin panel (if enabled in config.ini)
diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index d666b96d6e9..9fc55eff7fb 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -707,6 +707,7 @@ $config = [
                 'ipRegEx' => 'VuFind\Role\PermissionProvider\Factory::getIpRegEx',
                 'serverParam' => 'VuFind\Role\PermissionProvider\Factory::getServerParam',
                 'shibboleth' => 'VuFind\Role\PermissionProvider\Factory::getShibboleth',
+                'user' => 'VuFind\Role\PermissionProvider\Factory::getUser',
                 '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 f7aa828055a..478e6ab8440 100644
--- a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php
+++ b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php
@@ -108,4 +108,18 @@ class Factory
             $sm->getServiceLocator()->get('ZfcRbac\Service\AuthorizationService')
         );
     }
+    
+    /**
+     * Factory for User
+     *
+     * @param ServiceManager $sm Service manager.
+     *
+     * @return User
+     */
+    public static function getUser(ServiceManager $sm)
+    {
+        return new User(
+            $sm->getServiceLocator()->get('ZfcRbac\Service\AuthorizationService')
+        );
+    }
 }
diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/User.php b/module/VuFind/src/VuFind/Role/PermissionProvider/User.php
new file mode 100644
index 00000000000..817a07c3c55
--- /dev/null
+++ b/module/VuFind/src/VuFind/Role/PermissionProvider/User.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * User 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   Markus Beh <markus.beh@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 ZfcRbac\Service\AuthorizationService;
+
+/**
+ * LDAP permission provider for VuFind.
+ * based on permission provider Username.php
+ *
+ * @category VuFind2
+ * @package  Authorization
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://www.vufind.org  Main Page
+ */
+class User implements PermissionProviderInterface,
+    \Zend\Log\LoggerAwareInterface
+{
+    use \VuFind\Log\LoggerAwareTrait;
+
+    /**
+     * Authorization object
+     *
+     * @var AuthorizationService
+     */
+    protected $auth;
+
+    /**
+     * Constructor
+     *
+     * @param AuthorizationService $authorization Authorization service
+     */
+    public function __construct(AuthorizationService $authorization)
+    {
+        $this->auth = $authorization;
+    }
+
+    /**
+     * 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)
+    {
+        // If no user is logged in, or the user doesn't match the passed-in
+        // whitelist, we can't grant the permission to any roles.
+        if (!($user = $this->auth->getIdentity())) {
+            return [];
+        }
+
+        // which user attribute has to match which pattern to get permissions?
+        $criteria = [];
+        foreach ((array)$options as $option) {
+            $parts = explode(' ', $option, 2);
+            if (count($parts) < 2) {
+                $this->logError("configuration option '{$option}' invalid");
+                return [];
+            } else {
+                list($attribute, $pattern) = $parts;
+
+                // check user attribute values against the pattern
+                if (! preg_match('/^\/.*\/$/', $pattern)) {
+                    $pattern = '/' . $pattern . '/';
+                }
+
+                if (preg_match($pattern, $user[$attribute])) {
+                    return ['loggedin'];
+                }
+            }
+        }
+
+        //no matches found, so the user don't get any permissions
+        return [];
+    }
+}
diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php
new file mode 100644
index 00000000000..4d997c636f3
--- /dev/null
+++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * PermissionProvider User 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 VuFind
+ * @package  Tests
+ * @author   Markus Beh <markus.beh@ub.uni-freiburg.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+namespace VuFindTest\Role\PermissionProvider;
+
+use ZfcRbac\Service\AuthorizationService;
+
+/**
+ * PermissionProvider User Test Class
+ *
+ * @category VuFind
+ * @package  Tests
+ * @author   Markus Beh <markus.beh@ub.uni-freiburg.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development:testing:unit_tests Wiki
+ */
+class UserTest extends \VuFindTest\Unit\TestCase
+{
+    protected $userValueMap = [
+        'testuser1' =>
+        [
+                ['username','mbeh'],
+                ['email','markus.beh@ub.uni-freiburg.de'],
+                ['college', 'Albert Ludwigs Universität Freiburg']
+        ]
+        ,
+        'testuser2' =>
+        [
+                ['username','mbeh2'],
+                ['email','markus.beh@ub.uni-freiburg.de'],
+                ['college', 'Villanova University'],
+                ['major', 'alumni']
+        ]
+    ];
+
+
+    /**
+     * Test single option with matching string
+     *
+     * @return void
+     */
+    public function testGetPermissions()
+    {
+        $this->check(
+            'testuser1',
+            ['college .*Freiburg'],
+            ['loggedin']
+        );
+
+        $this->check(
+            'testuser2',
+            ["college .*Freiburg"],
+            []
+        );
+    }
+
+    /**
+     * Test an invalid configuration
+     *
+     * @return void
+     */
+    public function testBadConfig()
+    {
+        $this->check(
+            'testuser1',
+            ['college'],
+            []
+        );
+    }
+
+    /**
+     * Convenience method for executing similiar tests
+     *
+     * @param string $testuser Name of testuser
+     * @param array  $options  Options like settings in permissions.ini
+     * @param array  $roles    Roles to return if match
+     *
+     * @return void
+     */
+    protected function check($testuser, $options, $roles)
+    {
+        $this->testuser
+            = (isset($this->userValueMap[$testuser]))
+            ? $testuser
+            : 'testuser1';
+
+        $auth = $this->getMockAuthorizationService();
+        $this->permissionProvider
+            = new \VuFind\Role\PermissionProvider\User($auth);
+
+        $this->assertEquals(
+            $roles,
+            $this->permissionProvider->getPermissions($options)
+        );
+    }
+
+    /**
+     * Get a mock authorization service object
+     *
+     * @return AuthorizationService
+     */
+    protected function getMockAuthorizationService()
+    {
+        $authorizationService
+            = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService')
+                ->disableOriginalConstructor()
+                ->getMock();
+        $authorizationService
+            ->method('getIdentity')
+            ->will($this->returnValue($this->getMockUser()));
+
+        return $authorizationService;
+    }
+
+    /**
+     * Get a mock user object
+     *
+     * @return UserRow
+     */
+    protected function getMockUser()
+    {
+        $user = $this->getMockBuilder('VuFind\Db\Row\User')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $user->method('__get')
+            ->will($this->returnValueMap($this->userValueMap[$this->testuser]));
+        $user->method('offsetGet')
+            ->will($this->returnValueMap($this->userValueMap[$this->testuser]));
+
+        return $user;
+    }
+
+
+}
-- 
GitLab