Passed
Push — master ( 730af0...3b14ce )
by Roeland
12:25 queued 11s
created

Internal::startSession()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
dl 0
loc 5
rs 10
c 1
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Arthur Schiwon <[email protected]>
9
 * @author cetra3 <[email protected]>
10
 * @author Christoph Wurst <[email protected]>
11
 * @author Lukas Reschke <[email protected]>
12
 * @author MartB <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Thomas Müller <[email protected]>
17
 * @author Victor Dubiniuk <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program. If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OC\Session;
36
37
use OC\Authentication\Exceptions\InvalidTokenException;
38
use OC\Authentication\Token\IProvider;
39
use OC\SystemConfig;
40
use OCP\IConfig;
41
use OCP\Session\Exceptions\SessionNotAvailableException;
42
43
/**
44
 * Class Internal
45
 *
46
 * wrap php's internal session handling into the Session interface
47
 *
48
 * @package OC\Session
49
 */
50
class Internal extends Session {
51
	/**
52
	 * @param string $name
53
	 * @throws \Exception
54
	 */
55
	public function __construct(string $name) {
56
		set_error_handler([$this, 'trapError']);
57
		$this->invoke('session_name', [$name]);
58
		try {
59
			$this->startSession();
60
		} catch (\Exception $e) {
61
			setcookie($this->invoke('session_name'), '', -1, \OC::$WEBROOT ?: '/');
62
		}
63
		restore_error_handler();
64
		if (!isset($_SESSION)) {
65
			throw new \Exception('Failed to start session');
66
		}
67
	}
68
69
	/**
70
	 * @param string $key
71
	 * @param integer $value
72
	 */
73
	public function set(string $key, $value) {
74
		$this->validateSession();
75
		$_SESSION[$key] = $value;
76
	}
77
78
	/**
79
	 * @param string $key
80
	 * @return mixed
81
	 */
82
	public function get(string $key) {
83
		if (!$this->exists($key)) {
84
			return null;
85
		}
86
		return $_SESSION[$key];
87
	}
88
89
	/**
90
	 * @param string $key
91
	 * @return bool
92
	 */
93
	public function exists(string $key): bool {
94
		return isset($_SESSION[$key]);
95
	}
96
97
	/**
98
	 * @param string $key
99
	 */
100
	public function remove(string $key) {
101
		if (isset($_SESSION[$key])) {
102
			unset($_SESSION[$key]);
103
		}
104
	}
105
106
	public function clear() {
107
		$this->invoke('session_unset');
108
		$this->regenerateId();
109
		$this->startSession();
110
		$_SESSION = [];
111
	}
112
113
	public function close() {
114
		$this->invoke('session_write_close');
115
		parent::close();
116
	}
117
118
	/**
119
	 * Wrapper around session_regenerate_id
120
	 *
121
	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
122
	 * @param bool $updateToken Wheater to update the associated auth token
123
	 * @return void
124
	 */
125
	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
126
		$oldId = null;
127
128
		if ($updateToken) {
129
			// Get the old id to update the token
130
			try {
131
				$oldId = $this->getId();
132
			} catch (SessionNotAvailableException $e) {
133
				// We can't update a token if there is no previous id
134
				$updateToken = false;
135
			}
136
		}
137
138
		try {
139
			@session_regenerate_id($deleteOldSession);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for session_regenerate_id(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

139
			/** @scrutinizer ignore-unhandled */ @session_regenerate_id($deleteOldSession);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
140
		} catch (\Error $e) {
141
			$this->trapError($e->getCode(), $e->getMessage());
142
		}
143
144
		if ($updateToken) {
145
			// Get the new id to update the token
146
			$newId = $this->getId();
147
148
			/** @var IProvider $tokenProvider */
149
			$tokenProvider = \OC::$server->query(IProvider::class);
150
151
			try {
152
				$tokenProvider->renewSessionToken($oldId, $newId);
153
			} catch (InvalidTokenException $e) {
154
				// Just ignore
155
			}
156
		}
157
	}
158
159
	/**
160
	 * Wrapper around session_id
161
	 *
162
	 * @return string
163
	 * @throws SessionNotAvailableException
164
	 * @since 9.1.0
165
	 */
166
	public function getId(): string {
167
		$id = $this->invoke('session_id', [], true);
168
		if ($id === '') {
169
			throw new SessionNotAvailableException();
170
		}
171
		return $id;
172
	}
173
174
	/**
175
	 * @throws \Exception
176
	 */
177
	public function reopen() {
178
		throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
179
	}
180
181
	/**
182
	 * @param int $errorNumber
183
	 * @param string $errorString
184
	 * @throws \ErrorException
185
	 */
186
	public function trapError(int $errorNumber, string $errorString) {
187
		throw new \ErrorException($errorString);
188
	}
189
190
	/**
191
	 * @throws \Exception
192
	 */
193
	private function validateSession() {
194
		if ($this->sessionClosed) {
195
			throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed');
196
		}
197
	}
198
199
	/**
200
	 * @param string $functionName the full session_* function name
201
	 * @param array $parameters
202
	 * @param bool $silence whether to suppress warnings
203
	 * @throws \ErrorException via trapError
204
	 * @return mixed
205
	 */
206
	private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
207
		try {
208
			if($silence) {
209
				return @call_user_func_array($functionName, $parameters);
210
			} else {
211
				return call_user_func_array($functionName, $parameters);
212
			}
213
		} catch(\Error $e) {
214
			$this->trapError($e->getCode(), $e->getMessage());
215
		}
216
	}
217
218
	private function startSession() {
219
		if (PHP_VERSION_ID < 70300) {
220
			$this->invoke('session_start');
221
		} else {
222
			$this->invoke('session_start', [['cookie_samesite' => 'Lax']]);
223
		}
224
	}
225
}
226