Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SecureDelegator
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 12
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 close
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 destroy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 gc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 open
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 read
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 write
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 enableWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 disableWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDbTableManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setDbTableManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __call
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Secure session delegator
5 *
6 * Copyright (C) Villanova University 2018,
7 *               Leipzig University Library <info@ub.uni-leipzig.de> 2018.
8 *
9 * PHP version 8
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2,
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 *
24 * @category VuFind
25 * @package  Session_Handlers
26 * @author   Demian Katz <demian.katz@villanova.edu>
27 * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
28 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
29 * @link     https://vufind.org/wiki/development:plugins:session_handlers Wiki
30 */
31
32namespace VuFind\Session;
33
34use Laminas\Crypt\BlockCipher;
35use Laminas\Math\Rand;
36use VuFind\Cookie\CookieManager;
37use VuFind\Db\Table\PluginManager;
38
39use function func_get_args;
40
41/**
42 * Secure session delegator
43 *
44 * @category VuFind
45 * @package  Session_Handlers
46 * @author   Demian Katz <demian.katz@villanova.edu>
47 * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
48 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
49 * @link     https://vufind.org/wiki/development:plugins:session_handlers Wiki
50 */
51class SecureDelegator implements HandlerInterface
52{
53    /**
54     * The block cipher for en/decrypting session data.
55     *
56     * @var BlockCipher
57     */
58    protected $cipher;
59
60    /**
61     * VuFind cookie manager service.
62     *
63     * @var CookieManager
64     */
65    protected $cookieManager;
66
67    /**
68     * The wrapped session handler.
69     *
70     * @var HandlerInterface
71     */
72    protected $handler;
73
74    /**
75     * SecureDelegator constructor.
76     *
77     * @param CookieManager    $cookieManager VuFind cookie manager service.
78     * @param HandlerInterface $handler       The wrapped session handler.
79     */
80    public function __construct(
81        CookieManager $cookieManager,
82        HandlerInterface $handler
83    ) {
84        $this->handler = $handler;
85        $this->cookieManager = $cookieManager;
86        $this->cipher = BlockCipher::factory('openssl');
87    }
88
89    /**
90     * Closes a session.
91     *
92     * @return bool
93     */
94    public function close(): bool
95    {
96        return $this->__call(__FUNCTION__, []);
97    }
98
99    /**
100     * Destroys a session.
101     *
102     * @param string $id Session ID
103     *
104     * @return bool
105     */
106    public function destroy(string $id): bool
107    {
108        return $this->__call(__FUNCTION__, func_get_args());
109    }
110
111    /**
112     * Performs garbage collection.
113     *
114     * @param int $max_lifetime Maximum session life time
115     *
116     * @return int|false
117     */
118    public function gc(int $max_lifetime): int|false
119    {
120        return $this->__call(__FUNCTION__, func_get_args());
121    }
122
123    /**
124     * Opens a session.
125     *
126     * @param string $save_path Session save path
127     * @param string $name      Session name
128     *
129     * @return bool
130     */
131    public function open($save_path, $name): bool
132    {
133        $cookieName = "{$name}_KEY";
134        $cipherKey = ($cookieValue = $this->cookieManager->get($cookieName))
135            ?? base64_encode(Rand::getBytes(64));
136
137        if (!$cookieValue) {
138            $lifetime = session_get_cookie_params()['lifetime'];
139            $expire = $lifetime ? $lifetime + time() : 0;
140            $this->cookieManager->set($cookieName, $cipherKey, $expire);
141        }
142
143        $this->cipher->setKey(base64_decode($cipherKey));
144        return $this->handler->open($save_path, $name);
145    }
146
147    /**
148     * Read a sessions data.
149     *
150     * @param string $session_id Session id
151     *
152     * @return string|false
153     */
154    public function read($session_id): string|false
155    {
156        $data = $this->handler->read($session_id);
157        return $data ? ($this->cipher->decrypt($data) ?: '') : $data;
158    }
159
160    /**
161     * Writes session data.
162     *
163     * @param string $session_id   Session id
164     * @param string $session_data Session data
165     *
166     * @return bool
167     */
168    public function write($session_id, $session_data): bool
169    {
170        $data = $this->cipher->encrypt($session_data);
171        return $this->handler->write($session_id, $data);
172    }
173
174    /**
175     * Enable session writing (default)
176     *
177     * @return void
178     */
179    public function enableWrites()
180    {
181        $this->__call(__FUNCTION__, []);
182    }
183
184    /**
185     * Disable session writing, i.e. make it read-only
186     *
187     * @return void
188     */
189    public function disableWrites()
190    {
191        $this->__call(__FUNCTION__, []);
192    }
193
194    /**
195     * Get the plugin manager. Throw an exception if it is missing.
196     *
197     * @throws \Exception
198     * @return PluginManager
199     */
200    public function getDbTableManager()
201    {
202        return $this->__call(__FUNCTION__, []);
203    }
204
205    /**
206     * Set the plugin manager.
207     *
208     * @param PluginManager $manager Plugin manager
209     *
210     * @return void
211     */
212    public function setDbTableManager(PluginManager $manager)
213    {
214        $this->__call(__FUNCTION__, func_get_args());
215    }
216
217    /**
218     * Pass calls to non-existing methods to the wrapped Handler
219     *
220     * @param string $name      Name of the method being called
221     * @param array  $arguments Passed Arguments
222     *
223     * @return mixed
224     */
225    public function __call($name, $arguments)
226    {
227        return $this->handler->{$name}(...$arguments);
228    }
229}