Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.50% covered (warning)
87.50%
63 / 72
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
PrimoPermissionHandler
87.50% covered (warning)
87.50%
63 / 72
76.92% covered (warning)
76.92%
10 / 13
44.28
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setInstCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 instCodeExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInstCode
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasPermission
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 checkConfig
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 checkLegacySettings
54.55% covered (warning)
54.55%
6 / 11
0.00% covered (danger)
0.00%
0 / 1
7.35
 getInstCodes
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 autodetectCode
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
9
 getDefaultCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultOnCampusRule
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getOnCampusRule
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 checkPermission
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * Primo Permission Handler.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2013.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Search
25 * @author   Oliver Goldschmidt <o.goldschmidt@tuhh.de>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org Main Site
28 */
29
30namespace VuFind\Search\Primo;
31
32use LmcRbacMvc\Service\AuthorizationServiceAwareTrait;
33
34use function in_array;
35use function is_array;
36
37/**
38 * Primo Permission Handler.
39 *
40 * @category VuFind
41 * @package  Search
42 * @author   Oliver Goldschmidt <o.goldschmidt@tuhh.de>
43 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
44 * @link     https://vufind.org Main Site
45 */
46class PrimoPermissionHandler
47{
48    use AuthorizationServiceAwareTrait;
49
50    /**
51     * Primo-Config for Institutions.
52     *
53     * @var array
54     */
55    protected $primoConfig;
56
57    /**
58     * Institution code applicable for the user
59     *
60     * @var string
61     */
62    protected $instCode = null;
63
64    /**
65     * Constructor.
66     *
67     * @param Laminas\Config\Config|array $primoPermConfig Primo-Config for
68     * Institutions
69     *
70     * @return void
71     */
72    public function __construct($primoPermConfig)
73    {
74        if ($primoPermConfig instanceof \Laminas\Config\Config) {
75            $primoPermConfig = $primoPermConfig->toArray();
76        }
77        $this->primoConfig = is_array($primoPermConfig) ? $primoPermConfig : [];
78        $this->checkLegacySettings();
79        $this->checkConfig();
80    }
81
82    /**
83     * Set the institution code (no autodetection)
84     *
85     * @param string $code Institutioncode
86     *
87     * @return void
88     */
89    public function setInstCode($code)
90    {
91        // If the code is valid, we'll set it; otherwise, we'll use "false" to
92        // clear instCode's null status and indicate that the setter has been used.
93        $this->instCode = ($this->instCodeExists($code) === true) ? $code : false;
94    }
95
96    /**
97     * Determine if a institution code is set in config file
98     *
99     * @param string $code Code to approve against config file
100     *
101     * @return bool
102     */
103    public function instCodeExists($code)
104    {
105        return in_array($code, $this->getInstCodes()) === true;
106    }
107
108    /**
109     * Determine the institution code
110     * Returns false, if no institution can get set
111     *
112     * @return string|boolean
113     */
114    public function getInstCode()
115    {
116        if ($this->instCode === null) {
117            $this->autodetectCode();
118        }
119        return $this->instCode;
120    }
121
122    /**
123     * Check if the user has permission
124     *
125     * @return bool
126     */
127    public function hasPermission()
128    {
129        $code = $this->getInstCode();
130        return false !== $code && $this->checkPermission($code) === true;
131    }
132
133    /**
134     * Checks the config file section for validity
135     *
136     * @return void
137     */
138    protected function checkConfig()
139    {
140        if (
141            isset($this->primoConfig['institutionCode'])
142            || isset($this->primoConfig['onCampusRule'])
143            || ($this->getDefaultCode() !== false)
144        ) {
145            return;
146        }
147
148        // If we reach this point, no institution code is set in config.
149        // Primo will not work without an institution code!
150        throw new \Exception(
151            'No institutionCode found. Please be sure that at least a '
152            . 'defaultCode is configured in section [Institutions] '
153            . 'in Primo.ini.'
154        );
155    }
156
157    /**
158     * Legacy settings support
159     *
160     * @return void
161     */
162    protected function checkLegacySettings()
163    {
164        // if we already have settings, ignore the legacy ones
165        if (
166            isset($this->primoConfig['defaultCode'])
167            || isset($this->primoConfig['onCampusRule'])
168        ) {
169            return;
170        }
171
172        // Handle legacy options
173        $codes = $this->primoConfig['code'] ?? [];
174        $regex = $this->primoConfig['regex'] ?? [];
175        if (!empty($codes) && !empty($regex)) {
176            throw new \Exception(
177                'Legacy [Institutions] settings detected.'
178                . ' Please run upgrade process or correct settings manually'
179                . ' in Primo.ini and permissions.ini.'
180            );
181        }
182    }
183
184    /**
185     * Gets all possible institution codes from config file
186     *
187     * @return array Array with valid Primo institution codes
188     */
189    protected function getInstCodes()
190    {
191        // Start with default code (if any):
192        $defaultCode = $this->getDefaultCode();
193        $codes = ($defaultCode !== false) ? [$defaultCode] : [];
194
195        // Add additional keys from relevant config sections:
196        foreach (['institutionCode', 'onCampusRule'] as $section) {
197            if (
198                isset($this->primoConfig[$section])
199                && is_array($this->primoConfig[$section])
200            ) {
201                $codes = array_merge(
202                    $codes,
203                    array_keys($this->primoConfig[$section])
204                );
205            }
206        }
207
208        return $codes;
209    }
210
211    /**
212     * Autodetects the permissions by configuration file
213     *
214     * @return void
215     */
216    protected function autodetectCode()
217    {
218        $authService = $this->getAuthorizationService();
219
220        // if no authorization service is available, don't do anything
221        if (!$authService) {
222            $this->instCode = false;
223            return;
224        }
225
226        // walk through the relevant config sections and check if one is granted
227        foreach (['institutionCode', 'onCampusRule'] as $section) {
228            if (
229                isset($this->primoConfig[$section])
230                && is_array($this->primoConfig[$section])
231            ) {
232                foreach ($this->primoConfig[$section] as $code => $permRule) {
233                    if ($authService->isGranted($permRule)) {
234                        $this->instCode = $code;
235                        return;
236                    }
237                }
238            }
239        }
240
241        // if no rule has matched until here, assume the user gets the default code
242        if ($this->getDefaultCode() !== false) {
243            $this->instCode = $this->getDefaultCode();
244            return;
245        }
246
247        // Autodetection failed, set instCode to false
248        // Primo will not work without an institution code!
249        if ($this->instCode === null) {
250            $this->instCode = false;
251        }
252    }
253
254    /**
255     * Determine the default institution code
256     * Returns false, if no default code has been set
257     *
258     * @return string|boolean
259     */
260    protected function getDefaultCode()
261    {
262        return $this->primoConfig['defaultCode'] ?? false;
263    }
264
265    /**
266     * Determine the default onCampus Rule
267     *
268     * @return string
269     */
270    protected function getDefaultOnCampusRule()
271    {
272        $defaultCode = $this->getDefaultCode();
273        return ($defaultCode !== false)
274            ? $this->getOnCampusRule($defaultCode) : null;
275    }
276
277    /**
278     * Determine a onCampus Rule for a certain code
279     *
280     * @param string $code Code to determine the rule name for
281     *
282     * @return string
283     */
284    protected function getOnCampusRule($code)
285    {
286        if ($code === null) {
287            return null;
288        }
289
290        $onCampusRule
291            = $this->primoConfig['onCampusRule'][$code] ?? false;
292
293        if (false !== $onCampusRule) {
294            return $onCampusRule;
295        }
296
297        // If primoConfig->onCampusRule[] is not set
298        // no rule can get applied.
299        // So return null to indicate that nothing can get matched.
300        return null;
301    }
302
303    /**
304     * Checks, if a rule is granted
305     *
306     * @param string $code Code to check the rule name for
307     *
308     * @return bool
309     */
310    protected function checkPermission($code)
311    {
312        $onCampusRule = $this->getOnCampusRule($code);
313        $authService = $this->getAuthorizationService();
314
315        // if no authorization service is available, the user can't get permission
316        return $authService && $authService->isGranted($onCampusRule);
317    }
318}