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