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