Merge "Improve docs for Title::getInternalURL/getCanonicalURL"
[lhc/web/wiklou.git] / includes / session / SessionInfo.php
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 /**
27 * Value object returned by SessionProvider
28 *
29 * This holds the data necessary to construct a Session.
30 *
31 * @ingroup Session
32 * @since 1.27
33 */
34 class SessionInfo {
35 /** Minimum allowed priority */
36 const MIN_PRIORITY = 1;
37
38 /** Maximum allowed priority */
39 const MAX_PRIORITY = 100;
40
41 /** @var SessionProvider|null */
42 private $provider;
43
44 /** @var string */
45 private $id;
46
47 /** @var int */
48 private $priority;
49
50 /** @var UserInfo|null */
51 private $userInfo = null;
52
53 /** @var bool */
54 private $persisted = false;
55
56 /** @var bool */
57 private $remembered = false;
58
59 /** @var bool */
60 private $forceHTTPS = false;
61
62 /** @var bool */
63 private $idIsSafe = false;
64
65 /** @var bool */
66 private $forceUse = false;
67
68 /** @var array|null */
69 private $providerMetadata = null;
70
71 /**
72 * @param int $priority Session priority
73 * @param array $data
74 * - provider: (SessionProvider|null) If not given, the provider will be
75 * determined from the saved session data.
76 * - id: (string|null) Session ID
77 * - userInfo: (UserInfo|null) User known from the request. If
78 * $provider->canChangeUser() is false, a verified user
79 * must be provided.
80 * - persisted: (bool) Whether this session was persisted
81 * - remembered: (bool) Whether the verified user was remembered.
82 * Defaults to true.
83 * - forceHTTPS: (bool) Whether to force HTTPS for this session
84 * - metadata: (array) Provider metadata, to be returned by
85 * Session::getProviderMetadata(). See SessionProvider::mergeMetadata()
86 * and SessionProvider::refreshSessionInfo().
87 * - idIsSafe: (bool) Set true if the 'id' did not come from the user.
88 * Generally you'll use this from SessionProvider::newEmptySession(),
89 * and not from any other method.
90 * - forceUse: (bool) Set true if the 'id' is from
91 * SessionProvider::hashToSessionId() to delete conflicting session
92 * store data instead of discarding this SessionInfo. Ignored unless
93 * both 'provider' and 'id' are given.
94 * - copyFrom: (SessionInfo) SessionInfo to copy other data items from.
95 */
96 public function __construct( $priority, array $data ) {
97 if ( $priority < self::MIN_PRIORITY || $priority > self::MAX_PRIORITY ) {
98 throw new \InvalidArgumentException( 'Invalid priority' );
99 }
100
101 if ( isset( $data['copyFrom'] ) ) {
102 $from = $data['copyFrom'];
103 if ( !$from instanceof SessionInfo ) {
104 throw new \InvalidArgumentException( 'Invalid copyFrom' );
105 }
106 $data += [
107 'provider' => $from->provider,
108 'id' => $from->id,
109 'userInfo' => $from->userInfo,
110 'persisted' => $from->persisted,
111 'remembered' => $from->remembered,
112 'forceHTTPS' => $from->forceHTTPS,
113 'metadata' => $from->providerMetadata,
114 'idIsSafe' => $from->idIsSafe,
115 'forceUse' => $from->forceUse,
116 // @codeCoverageIgnoreStart
117 ];
118 // @codeCoverageIgnoreEnd
119 } else {
120 $data += [
121 'provider' => null,
122 'id' => null,
123 'userInfo' => null,
124 'persisted' => false,
125 'remembered' => true,
126 'forceHTTPS' => false,
127 'metadata' => null,
128 'idIsSafe' => false,
129 'forceUse' => false,
130 // @codeCoverageIgnoreStart
131 ];
132 // @codeCoverageIgnoreEnd
133 }
134
135 if ( $data['id'] !== null && !SessionManager::validateSessionId( $data['id'] ) ) {
136 throw new \InvalidArgumentException( 'Invalid session ID' );
137 }
138
139 if ( $data['userInfo'] !== null && !$data['userInfo'] instanceof UserInfo ) {
140 throw new \InvalidArgumentException( 'Invalid userInfo' );
141 }
142
143 if ( !$data['provider'] && $data['id'] === null ) {
144 throw new \InvalidArgumentException(
145 'Must supply an ID when no provider is given'
146 );
147 }
148
149 if ( $data['metadata'] !== null && !is_array( $data['metadata'] ) ) {
150 throw new \InvalidArgumentException( 'Invalid metadata' );
151 }
152
153 $this->provider = $data['provider'];
154 if ( $data['id'] !== null ) {
155 $this->id = $data['id'];
156 $this->idIsSafe = $data['idIsSafe'];
157 $this->forceUse = $data['forceUse'] && $this->provider;
158 } else {
159 $this->id = $this->provider->getManager()->generateSessionId();
160 $this->idIsSafe = true;
161 $this->forceUse = false;
162 }
163 $this->priority = (int)$priority;
164 $this->userInfo = $data['userInfo'];
165 $this->persisted = (bool)$data['persisted'];
166 if ( $data['provider'] !== null ) {
167 if ( $this->userInfo !== null && !$this->userInfo->isAnon() && $this->userInfo->isVerified() ) {
168 $this->remembered = (bool)$data['remembered'];
169 }
170 $this->providerMetadata = $data['metadata'];
171 }
172 $this->forceHTTPS = (bool)$data['forceHTTPS'];
173 }
174
175 /**
176 * Return the provider
177 * @return SessionProvider|null
178 */
179 final public function getProvider() {
180 return $this->provider;
181 }
182
183 /**
184 * Return the session ID
185 * @return string
186 */
187 final public function getId() {
188 return $this->id;
189 }
190
191 /**
192 * Indicate whether the ID is "safe"
193 *
194 * The ID is safe in the following cases:
195 * - The ID was randomly generated by the constructor.
196 * - The ID was found in the backend data store.
197 * - $this->getProvider()->persistsSessionId() is false.
198 * - The constructor was explicitly told it's safe using the 'idIsSafe'
199 * parameter.
200 *
201 * @return bool
202 */
203 final public function isIdSafe() {
204 return $this->idIsSafe;
205 }
206
207 /**
208 * Force use of this SessionInfo if validation fails
209 *
210 * The normal behavior is to discard the SessionInfo if validation against
211 * the data stored in the session store fails. If this returns true,
212 * SessionManager will instead delete the session store data so this
213 * SessionInfo may still be used. This is important for providers which use
214 * deterministic IDs and so cannot just generate a random new one.
215 *
216 * @return bool
217 */
218 final public function forceUse() {
219 return $this->forceUse;
220 }
221
222 /**
223 * Return the priority
224 * @return int
225 */
226 final public function getPriority() {
227 return $this->priority;
228 }
229
230 /**
231 * Return the user
232 * @return UserInfo|null
233 */
234 final public function getUserInfo() {
235 return $this->userInfo;
236 }
237
238 /**
239 * Return whether the session is persisted
240 * @return bool
241 */
242 final public function wasPersisted() {
243 return $this->persisted;
244 }
245
246 /**
247 * Return provider metadata
248 * @return array|null
249 */
250 final public function getProviderMetadata() {
251 return $this->providerMetadata;
252 }
253
254 /**
255 * Return whether the user was remembered
256 *
257 * For providers that can persist the user separately from the session,
258 * the human using it may not actually *want* that to be done. For example,
259 * a cookie-based provider can set cookies that are longer-lived than the
260 * backend session data, but on a public terminal the human likely doesn't
261 * want those cookies set.
262 *
263 * This is false unless a non-anonymous verified user was passed to
264 * the SessionInfo constructor by the provider, and the provider didn't
265 * pass false for the 'remembered' data item.
266 *
267 * @return bool
268 */
269 final public function wasRemembered() {
270 return $this->remembered;
271 }
272
273 /**
274 * Whether this session should only be used over HTTPS
275 * @return bool
276 */
277 final public function forceHTTPS() {
278 return $this->forceHTTPS;
279 }
280
281 public function __toString() {
282 return '[' . $this->getPriority() . ']' .
283 ( $this->getProvider() ?: 'null' ) .
284 ( $this->userInfo ?: '<null>' ) . $this->getId();
285 }
286
287 /**
288 * Compare two SessionInfo objects by priority
289 * @param SessionInfo $a
290 * @param SessionInfo $b
291 * @return int Negative if $a < $b, positive if $a > $b, zero if equal
292 */
293 public static function compare( $a, $b ) {
294 return $a->getPriority() <=> $b->getPriority();
295 }
296
297 }