Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
45 / 45 |
|
100.00% |
8 / 8 |
CRAP | |
100.00% |
1 / 1 |
ImmutableSessionProviderWithCookie | |
100.00% |
45 / 45 |
|
100.00% |
8 / 8 |
22 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
getSessionIdFromCookie | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
persistsSessionId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
canChangeUser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
persistSession | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
unpersistSession | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
getVaryCookies | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
whyNoSession | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * MediaWiki session provider base class |
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 MediaWiki\MainConfigNames; |
28 | use MediaWiki\Request\WebRequest; |
29 | |
30 | /** |
31 | * An ImmutableSessionProviderWithCookie doesn't persist the user, but |
32 | * optionally can use a cookie to support multiple IDs per session. |
33 | * |
34 | * As mentioned in the documentation for SessionProvider, many methods that are |
35 | * technically "cannot persist ID" could be turned into "can persist ID but |
36 | * not changing User" using a session cookie. This class implements such an |
37 | * optional session cookie. |
38 | * |
39 | * @stable to extend |
40 | * @ingroup Session |
41 | * @since 1.27 |
42 | */ |
43 | abstract class ImmutableSessionProviderWithCookie extends SessionProvider { |
44 | |
45 | /** @var string|null */ |
46 | protected $sessionCookieName = null; |
47 | /** @var mixed[] */ |
48 | protected $sessionCookieOptions = []; |
49 | |
50 | /** |
51 | * @stable to call |
52 | * @param array $params Keys include: |
53 | * - sessionCookieName: Session cookie name, if multiple sessions per |
54 | * client are to be supported. |
55 | * - sessionCookieOptions: Options to pass to WebResponse::setCookie(). |
56 | */ |
57 | public function __construct( $params = [] ) { |
58 | parent::__construct(); |
59 | |
60 | if ( isset( $params['sessionCookieName'] ) ) { |
61 | if ( !is_string( $params['sessionCookieName'] ) ) { |
62 | throw new InvalidArgumentException( 'sessionCookieName must be a string' ); |
63 | } |
64 | $this->sessionCookieName = $params['sessionCookieName']; |
65 | } |
66 | if ( isset( $params['sessionCookieOptions'] ) ) { |
67 | if ( !is_array( $params['sessionCookieOptions'] ) ) { |
68 | throw new InvalidArgumentException( 'sessionCookieOptions must be an array' ); |
69 | } |
70 | $this->sessionCookieOptions = $params['sessionCookieOptions']; |
71 | } |
72 | } |
73 | |
74 | /** |
75 | * Get the session ID from the cookie, if any. |
76 | * |
77 | * Only call this if $this->sessionCookieName !== null. If |
78 | * sessionCookieName is null, do some logic (probably involving a call to |
79 | * $this->hashToSessionId()) to create the single session ID corresponding |
80 | * to this WebRequest instead of calling this method. |
81 | * |
82 | * @param WebRequest $request |
83 | * @return string|null |
84 | */ |
85 | protected function getSessionIdFromCookie( WebRequest $request ) { |
86 | if ( $this->sessionCookieName === null ) { |
87 | throw new \BadMethodCallException( |
88 | __METHOD__ . ' may not be called when $this->sessionCookieName === null' |
89 | ); |
90 | } |
91 | |
92 | $prefix = $this->sessionCookieOptions['prefix'] |
93 | ?? $this->getConfig()->get( MainConfigNames::CookiePrefix ); |
94 | $id = $request->getCookie( $this->sessionCookieName, $prefix ); |
95 | return SessionManager::validateSessionId( $id ) ? $id : null; |
96 | } |
97 | |
98 | /** |
99 | * @inheritDoc |
100 | * @stable to override |
101 | */ |
102 | public function persistsSessionId() { |
103 | return $this->sessionCookieName !== null; |
104 | } |
105 | |
106 | /** |
107 | * @inheritDoc |
108 | * @stable to override |
109 | */ |
110 | public function canChangeUser() { |
111 | return false; |
112 | } |
113 | |
114 | /** |
115 | * @inheritDoc |
116 | * @stable to override |
117 | */ |
118 | public function persistSession( SessionBackend $session, WebRequest $request ) { |
119 | if ( $this->sessionCookieName === null ) { |
120 | return; |
121 | } |
122 | |
123 | $response = $request->response(); |
124 | if ( $response->headersSent() ) { |
125 | // Can't do anything now |
126 | $this->logger->debug( __METHOD__ . ': Headers already sent' ); |
127 | return; |
128 | } |
129 | |
130 | $options = $this->sessionCookieOptions; |
131 | if ( $session->shouldForceHTTPS() || $session->getUser()->requiresHTTPS() ) { |
132 | // Send a cookie unless $wgForceHTTPS is set (T256095) |
133 | if ( !$this->getConfig()->get( MainConfigNames::ForceHTTPS ) ) { |
134 | $response->setCookie( 'forceHTTPS', 'true', null, |
135 | [ 'prefix' => '', 'secure' => false ] + $options ); |
136 | } |
137 | $options['secure'] = true; |
138 | } |
139 | |
140 | $response->setCookie( $this->sessionCookieName, $session->getId(), null, $options ); |
141 | } |
142 | |
143 | /** |
144 | * @inheritDoc |
145 | * @stable to override |
146 | */ |
147 | public function unpersistSession( WebRequest $request ) { |
148 | if ( $this->sessionCookieName === null ) { |
149 | return; |
150 | } |
151 | |
152 | $response = $request->response(); |
153 | if ( $response->headersSent() ) { |
154 | // Can't do anything now |
155 | $this->logger->debug( __METHOD__ . ': Headers already sent' ); |
156 | return; |
157 | } |
158 | |
159 | $response->clearCookie( $this->sessionCookieName, $this->sessionCookieOptions ); |
160 | } |
161 | |
162 | /** |
163 | * @inheritDoc |
164 | * @stable to override |
165 | */ |
166 | public function getVaryCookies() { |
167 | if ( $this->sessionCookieName === null ) { |
168 | return []; |
169 | } |
170 | |
171 | $prefix = $this->sessionCookieOptions['prefix'] ?? |
172 | $this->getConfig()->get( MainConfigNames::CookiePrefix ); |
173 | return [ $prefix . $this->sessionCookieName ]; |
174 | } |
175 | |
176 | public function whyNoSession() { |
177 | return wfMessage( 'sessionprovider-nocookies' ); |
178 | } |
179 | } |