Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
66 / 66 |
|
100.00% |
13 / 13 |
CRAP | |
100.00% |
1 / 1 |
SessionInfo | |
100.00% |
66 / 66 |
|
100.00% |
13 / 13 |
33 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
52 / 52 |
|
100.00% |
1 / 1 |
19 | |||
getProvider | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isIdSafe | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
forceUse | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPriority | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUserInfo | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
wasPersisted | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getProviderMetadata | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
wasRemembered | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
forceHTTPS | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
compare | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * MediaWiki session info |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup Session |
22 | */ |
23 | |
24 | namespace MediaWiki\Session; |
25 | |
26 | use InvalidArgumentException; |
27 | use Stringable; |
28 | |
29 | /** |
30 | * Value object returned by SessionProvider |
31 | * |
32 | * This holds the data necessary to construct a Session. |
33 | * May require services to be injected into the constructor. |
34 | * |
35 | * @newable |
36 | * |
37 | * @ingroup Session |
38 | * @since 1.27 |
39 | */ |
40 | class SessionInfo implements Stringable { |
41 | /** Minimum allowed priority */ |
42 | public const MIN_PRIORITY = 1; |
43 | |
44 | /** Maximum allowed priority */ |
45 | public const MAX_PRIORITY = 100; |
46 | |
47 | /** @var SessionProvider|null */ |
48 | private $provider; |
49 | |
50 | /** @var string */ |
51 | private $id; |
52 | |
53 | /** @var int */ |
54 | private $priority; |
55 | |
56 | /** @var UserInfo|null */ |
57 | private $userInfo = null; |
58 | |
59 | /** @var bool */ |
60 | private $persisted = false; |
61 | |
62 | /** @var bool */ |
63 | private $remembered = false; |
64 | |
65 | /** @var bool */ |
66 | private $forceHTTPS = false; |
67 | |
68 | /** @var bool */ |
69 | private $idIsSafe = false; |
70 | |
71 | /** @var bool */ |
72 | private $forceUse = false; |
73 | |
74 | /** @var array|null */ |
75 | private $providerMetadata = null; |
76 | |
77 | /** |
78 | * @stable to call |
79 | * |
80 | * @param int $priority Session priority |
81 | * @param array $data |
82 | * - provider: (SessionProvider|null) If not given, the provider will be |
83 | * determined from the saved session data. |
84 | * - id: (string|null) Session ID |
85 | * - userInfo: (UserInfo|null) User known from the request. If |
86 | * $provider->canChangeUser() is false, a verified user |
87 | * must be provided. |
88 | * - persisted: (bool) Whether this session was persisted |
89 | * - remembered: (bool) Whether the verified user was remembered. |
90 | * Defaults to true. |
91 | * - forceHTTPS: (bool) Whether to force HTTPS for this session. This is |
92 | * ignored if $wgForceHTTPS is true. |
93 | * - metadata: (array) Provider metadata, to be returned by |
94 | * Session::getProviderMetadata(). See SessionProvider::mergeMetadata() |
95 | * and SessionProvider::refreshSessionInfo(). |
96 | * - idIsSafe: (bool) Set true if the 'id' did not come from the user. |
97 | * Generally you'll use this from SessionProvider::newEmptySession(), |
98 | * and not from any other method. |
99 | * - forceUse: (bool) Set true if the 'id' is from |
100 | * SessionProvider::hashToSessionId() to delete conflicting session |
101 | * store data instead of discarding this SessionInfo. Ignored unless |
102 | * both 'provider' and 'id' are given. |
103 | * - copyFrom: (SessionInfo) SessionInfo to copy other data items from. |
104 | */ |
105 | public function __construct( $priority, array $data ) { |
106 | if ( $priority < self::MIN_PRIORITY || $priority > self::MAX_PRIORITY ) { |
107 | throw new InvalidArgumentException( 'Invalid priority' ); |
108 | } |
109 | |
110 | if ( isset( $data['copyFrom'] ) ) { |
111 | $from = $data['copyFrom']; |
112 | if ( !$from instanceof SessionInfo ) { |
113 | throw new InvalidArgumentException( 'Invalid copyFrom' ); |
114 | } |
115 | $data += [ |
116 | 'provider' => $from->provider, |
117 | 'id' => $from->id, |
118 | 'userInfo' => $from->userInfo, |
119 | 'persisted' => $from->persisted, |
120 | 'remembered' => $from->remembered, |
121 | 'forceHTTPS' => $from->forceHTTPS, |
122 | 'metadata' => $from->providerMetadata, |
123 | 'idIsSafe' => $from->idIsSafe, |
124 | 'forceUse' => $from->forceUse, |
125 | // @codeCoverageIgnoreStart |
126 | ]; |
127 | // @codeCoverageIgnoreEnd |
128 | } else { |
129 | $data += [ |
130 | 'provider' => null, |
131 | 'id' => null, |
132 | 'userInfo' => null, |
133 | 'persisted' => false, |
134 | 'remembered' => true, |
135 | 'forceHTTPS' => false, |
136 | 'metadata' => null, |
137 | 'idIsSafe' => false, |
138 | 'forceUse' => false, |
139 | // @codeCoverageIgnoreStart |
140 | ]; |
141 | // @codeCoverageIgnoreEnd |
142 | } |
143 | |
144 | if ( $data['id'] !== null && !SessionManager::validateSessionId( $data['id'] ) ) { |
145 | throw new InvalidArgumentException( 'Invalid session ID' ); |
146 | } |
147 | |
148 | if ( $data['userInfo'] !== null && !$data['userInfo'] instanceof UserInfo ) { |
149 | throw new InvalidArgumentException( 'Invalid userInfo' ); |
150 | } |
151 | |
152 | if ( !$data['provider'] && $data['id'] === null ) { |
153 | throw new InvalidArgumentException( |
154 | 'Must supply an ID when no provider is given' |
155 | ); |
156 | } |
157 | |
158 | if ( $data['metadata'] !== null && !is_array( $data['metadata'] ) ) { |
159 | throw new InvalidArgumentException( 'Invalid metadata' ); |
160 | } |
161 | |
162 | $this->provider = $data['provider']; |
163 | if ( $data['id'] !== null ) { |
164 | $this->id = $data['id']; |
165 | $this->idIsSafe = $data['idIsSafe']; |
166 | $this->forceUse = $data['forceUse'] && $this->provider; |
167 | } else { |
168 | $this->id = $this->provider->getManager()->generateSessionId(); |
169 | $this->idIsSafe = true; |
170 | $this->forceUse = false; |
171 | } |
172 | $this->priority = (int)$priority; |
173 | $this->userInfo = $data['userInfo']; |
174 | $this->persisted = (bool)$data['persisted']; |
175 | if ( $data['provider'] !== null ) { |
176 | if ( $this->userInfo !== null && !$this->userInfo->isAnon() && $this->userInfo->isVerified() ) { |
177 | $this->remembered = (bool)$data['remembered']; |
178 | } |
179 | $this->providerMetadata = $data['metadata']; |
180 | } |
181 | $this->forceHTTPS = (bool)$data['forceHTTPS']; |
182 | } |
183 | |
184 | /** |
185 | * Return the provider |
186 | * @return SessionProvider|null |
187 | */ |
188 | final public function getProvider() { |
189 | return $this->provider; |
190 | } |
191 | |
192 | /** |
193 | * Return the session ID |
194 | * @return string |
195 | */ |
196 | final public function getId() { |
197 | return $this->id; |
198 | } |
199 | |
200 | /** |
201 | * Indicate whether the ID is "safe" |
202 | * |
203 | * The ID is safe in the following cases: |
204 | * - The ID was randomly generated by the constructor. |
205 | * - The ID was found in the backend data store. |
206 | * - $this->getProvider()->persistsSessionId() is false. |
207 | * - The constructor was explicitly told it's safe using the 'idIsSafe' |
208 | * parameter. |
209 | * |
210 | * @return bool |
211 | */ |
212 | final public function isIdSafe() { |
213 | return $this->idIsSafe; |
214 | } |
215 | |
216 | /** |
217 | * Force use of this SessionInfo if validation fails |
218 | * |
219 | * The normal behavior is to discard the SessionInfo if validation against |
220 | * the data stored in the session store fails. If this returns true, |
221 | * SessionManager will instead delete the session store data so this |
222 | * SessionInfo may still be used. This is important for providers which use |
223 | * deterministic IDs and so cannot just generate a random new one. |
224 | * |
225 | * @return bool |
226 | */ |
227 | final public function forceUse() { |
228 | return $this->forceUse; |
229 | } |
230 | |
231 | /** |
232 | * Return the priority |
233 | * @return int |
234 | */ |
235 | final public function getPriority() { |
236 | return $this->priority; |
237 | } |
238 | |
239 | /** |
240 | * Return the user |
241 | * @return UserInfo|null |
242 | */ |
243 | final public function getUserInfo() { |
244 | return $this->userInfo; |
245 | } |
246 | |
247 | /** |
248 | * Return whether the session is persisted |
249 | * @return bool |
250 | */ |
251 | final public function wasPersisted() { |
252 | return $this->persisted; |
253 | } |
254 | |
255 | /** |
256 | * Return provider metadata |
257 | * @return array|null |
258 | */ |
259 | final public function getProviderMetadata() { |
260 | return $this->providerMetadata; |
261 | } |
262 | |
263 | /** |
264 | * Return whether the user was remembered |
265 | * |
266 | * For providers that can persist the user separately from the session, |
267 | * the human using it may not actually *want* that to be done. For example, |
268 | * a cookie-based provider can set cookies that are longer-lived than the |
269 | * backend session data, but on a public terminal the human likely doesn't |
270 | * want those cookies set. |
271 | * |
272 | * This is false unless a non-anonymous verified user was passed to |
273 | * the SessionInfo constructor by the provider, and the provider didn't |
274 | * pass false for the 'remembered' data item. |
275 | * |
276 | * @return bool |
277 | */ |
278 | final public function wasRemembered() { |
279 | return $this->remembered; |
280 | } |
281 | |
282 | /** |
283 | * Whether this session should only be used over HTTPS. This should be |
284 | * ignored if $wgForceHTTPS is true. |
285 | * |
286 | * @return bool |
287 | */ |
288 | final public function forceHTTPS() { |
289 | return $this->forceHTTPS; |
290 | } |
291 | |
292 | public function __toString() { |
293 | return '[' . $this->getPriority() . ']' . |
294 | ( $this->getProvider() ?: 'null' ) . |
295 | ( $this->userInfo ?: '<null>' ) . $this->getId(); |
296 | } |
297 | |
298 | /** |
299 | * Compare two SessionInfo objects by priority |
300 | * @param SessionInfo $a |
301 | * @param SessionInfo $b |
302 | * @return int Negative if $a < $b, positive if $a > $b, zero if equal |
303 | */ |
304 | public static function compare( $a, $b ) { |
305 | return $a->getPriority() <=> $b->getPriority(); |
306 | } |
307 | |
308 | } |