PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.6.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.6.0
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Session / SessionAuth.php
matomo / app / core / Session Last commit date
SaveHandler 4 years ago SessionAuth.php 4 years ago SessionFingerprint.php 5 years ago SessionInitializer.php 5 years ago SessionNamespace.php 5 years ago
SessionAuth.php
247 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9
10 namespace Piwik\Session;
11
12 use Piwik\Auth;
13 use Piwik\AuthResult;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\Container\StaticContainer;
17 use Piwik\Date;
18 use Piwik\Plugins\UsersManager\Model;
19 use Piwik\Plugins\UsersManager\Model as UsersModel;
20 use Piwik\Session;
21 use Psr\Log\LoggerInterface;
22
23 /**
24 * Validates already authenticated sessions.
25 *
26 * See {@link \Piwik\Session\SessionFingerprint} for more info.
27 */
28 class SessionAuth implements Auth
29 {
30 /**
31 * For tests, since there's no actual session there.
32 *
33 * @var bool
34 */
35 private $shouldDestroySession;
36
37 /**
38 * @var UsersModel
39 */
40 private $userModel;
41
42 /**
43 * Set internally so it can be queried in FrontController.
44 *
45 * @var array
46 */
47 private $user;
48
49 private $tokenAuth;
50
51 public function __construct(UsersModel $userModel = null, $shouldDestroySession = true)
52 {
53 $this->userModel = $userModel ?: new UsersModel();
54 $this->shouldDestroySession = $shouldDestroySession;
55 }
56
57 public function getName()
58 {
59 // empty
60 }
61
62 public function setTokenAuth($token_auth)
63 {
64 $this->tokenAuth = $token_auth;
65 }
66
67 public function getLogin()
68 {
69 if (isset($this->user['login'])) {
70 return $this->user['login'];
71 }
72 }
73
74 public function getTokenAuthSecret()
75 {
76 // empty
77 }
78
79 public function setLogin($login)
80 {
81 // empty
82 }
83
84 public function setPassword($password)
85 {
86 // empty
87 }
88
89 public function setPasswordHash($passwordHash)
90 {
91 // empty
92 }
93
94 public function authenticate()
95 {
96 $sessionFingerprint = new SessionFingerprint();
97 $userModel = $this->userModel;
98
99 $this->checkIfSessionFailedToRead();
100
101 if ($this->isExpiredSession($sessionFingerprint)) {
102 $sessionFingerprint->clear();
103 return $this->makeAuthFailure();
104 }
105
106 $userForSession = $sessionFingerprint->getUser();
107 if (empty($userForSession)) {
108 return $this->makeAuthFailure();
109 }
110
111 $user = $userModel->getUser($userForSession);
112 if (empty($user)
113 || $user['login'] !== $userForSession // sanity check in case there's a bug in getUser()
114 ) {
115 return $this->makeAuthFailure();
116 }
117
118 $tsPasswordModified = !empty($user['ts_password_modified']) ? $user['ts_password_modified'] : null;
119 if ($this->isSessionStartedBeforePasswordChange($sessionFingerprint, $tsPasswordModified)) {
120 $this->destroyCurrentSession($sessionFingerprint);
121 return $this->makeAuthFailure();
122 }
123
124 $this->updateSessionExpireTime($sessionFingerprint);
125
126 if ($this->tokenAuth !== null
127 && $this->tokenAuth !== false
128 && $this->tokenAuth !== $sessionFingerprint->getSessionTokenAuth()) {
129 return $this->makeAuthFailure();
130 }
131
132 if ($sessionFingerprint->getSessionTokenAuth()) {
133 $tokenAuth = $sessionFingerprint->getSessionTokenAuth();
134 } else {
135 $tokenAuth = $this->userModel->generateRandomTokenAuth();
136 }
137
138 return $this->makeAuthSuccess($user, $tokenAuth);
139 }
140
141 private function isSessionStartedBeforePasswordChange(SessionFingerprint $sessionFingerprint, $tsPasswordModified)
142 {
143 // sanity check, make sure users can still login if the ts_password_modified column does not exist
144 if ($tsPasswordModified === null) {
145 return false;
146 }
147
148 // if the session start time doesn't exist for some reason, log the user out
149 $sessionStartTime = $sessionFingerprint->getSessionStartTime();
150 if (empty($sessionStartTime)) {
151 return true;
152 }
153
154 return $sessionStartTime < Date::factory($tsPasswordModified)->getTimestampUTC();
155 }
156
157 private function makeAuthFailure()
158 {
159 return new AuthResult(AuthResult::FAILURE, null, null);
160 }
161
162 private function makeAuthSuccess($user, $tokenAuth)
163 {
164 $this->user = $user;
165 $this->tokenAuth = $tokenAuth;
166
167 $isSuperUser = (int) $user['superuser_access'];
168 $code = $isSuperUser ? AuthResult::SUCCESS_SUPERUSER_AUTH_CODE : AuthResult::SUCCESS;
169
170 return new AuthResult($code, $user['login'], $tokenAuth);
171 }
172
173 protected function initNewBlankSession(SessionFingerprint $sessionFingerprint)
174 {
175 // this user should be using a different session, so generate a new ID
176 // NOTE: Zend_Session cannot be used since it will destroy the old
177 // session.
178 if ($this->shouldDestroySession) {
179 session_regenerate_id();
180 }
181
182 // regenerating the ID will create a new session w/ a new ID, but will
183 // copy over the existing session data. we want the new session for the
184 // unauthorized user to be different, so we clear the session fingerprint.
185 $sessionFingerprint->clear();
186 }
187
188 protected function destroyCurrentSession(SessionFingerprint $sessionFingerprint)
189 {
190 // Note: Piwik will attempt to create another session in the LoginController
191 // when rendering the login form (the nonce for the form is stored in the session).
192 // So we can't use Session::destroy() since Zend prohibits starting a new session
193 // after session_destroy() is called. Instead we clear the session fingerprint for
194 // the existing session and generate a new session. Both the old session &
195 // new session should have no stored data.
196 $sessionFingerprint->clear();
197 if ($this->shouldDestroySession) {
198 Session::regenerateId();
199 }
200 }
201
202 public function getTokenAuth()
203 {
204 return $this->tokenAuth;
205 }
206
207 private function updateSessionExpireTime(SessionFingerprint $sessionFingerprint)
208 {
209 $sessionParams = session_get_cookie_params();
210
211 // we update the session cookie to make sure expired session cookies are not available client side...
212 $sessionCookieLifetime = Config::getInstance()->General['login_cookie_expire'];
213 Session::writeCookie(
214 session_name(),
215 session_id(),
216 time() + $sessionCookieLifetime,
217 $sessionParams['path'],
218 $sessionParams['domain'],
219 $sessionParams['secure'],
220 $sessionParams['httponly'],
221 Session::getSameSiteCookieValue()
222 );
223
224 // ...and we also update the expiration time stored server side so we can prevent expired sessions from being reused
225 $sessionFingerprint->updateSessionExpirationTime();
226 }
227
228 private function isExpiredSession(SessionFingerprint $sessionFingerprint)
229 {
230 $expirationTime = $sessionFingerprint->getExpirationTime();
231 if (empty($expirationTime)) {
232 return true;
233 }
234
235 $isExpired = Date::now()->getTimestampUTC() > $expirationTime;
236 return $isExpired;
237 }
238
239 private function checkIfSessionFailedToRead()
240 {
241 if (Session\SaveHandler\DbTable::$wasSessionToLargeToRead) {
242 StaticContainer::get(LoggerInterface::class)->warning(
243 "Too much data stored in the session so it could not be read properly. If you were logged out, this is why.");
244 }
245 }
246 }
247