PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.6.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.6.0
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 7 months ago SessionAuth.php 7 months ago SessionFingerprint.php 7 months ago SessionInitializer.php 1 year ago SessionNamespace.php 1 year ago
SessionAuth.php
199 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 public function __construct(?UsersModel $userModel = null, $shouldDestroySession = \true)
44 {
45 $this->userModel = $userModel ?: new UsersModel();
46 $this->shouldDestroySession = $shouldDestroySession;
47 }
48 public function getName()
49 {
50 // empty
51 }
52 public function setTokenAuth(
53 #[\SensitiveParameter]
54 $token_auth)
55 {
56 $this->tokenAuth = $token_auth;
57 }
58 public function getLogin()
59 {
60 if (isset($this->user['login'])) {
61 return $this->user['login'];
62 }
63 }
64 public function getTokenAuthSecret()
65 {
66 // empty
67 }
68 public function setLogin($login)
69 {
70 // empty
71 }
72 public function setPassword(
73 #[\SensitiveParameter]
74 $password)
75 {
76 // empty
77 }
78 public function setPasswordHash(
79 #[\SensitiveParameter]
80 $passwordHash)
81 {
82 // empty
83 }
84 public function authenticate()
85 {
86 $sessionFingerprint = new \Piwik\Session\SessionFingerprint();
87 $userModel = $this->userModel;
88 $this->checkIfSessionFailedToRead();
89 if ($this->isExpiredSession($sessionFingerprint)) {
90 $sessionFingerprint->clear();
91 return $this->makeAuthFailure();
92 }
93 $userForSession = $sessionFingerprint->getUser();
94 if (empty($userForSession)) {
95 return $this->makeAuthFailure();
96 }
97 $user = $userModel->getUser($userForSession);
98 if (empty($user) || $user['login'] !== $userForSession) {
99 return $this->makeAuthFailure();
100 }
101 $tsPasswordModified = !empty($user['ts_password_modified']) ? $user['ts_password_modified'] : null;
102 if ($this->isSessionStartedBeforePasswordChange($sessionFingerprint, $tsPasswordModified)) {
103 $this->destroyCurrentSession($sessionFingerprint);
104 return $this->makeAuthFailure();
105 }
106 $this->updateSessionExpireTime($sessionFingerprint);
107 if ($this->tokenAuth !== null && $this->tokenAuth !== \false && $this->tokenAuth !== $sessionFingerprint->getSessionTokenAuth()) {
108 return $this->makeAuthFailure();
109 }
110 if ($sessionFingerprint->getSessionTokenAuth()) {
111 $tokenAuth = $sessionFingerprint->getSessionTokenAuth();
112 } else {
113 $tokenAuth = $this->userModel->generateRandomTokenAuth();
114 }
115 return $this->makeAuthSuccess($user, $tokenAuth);
116 }
117 private function isSessionStartedBeforePasswordChange(\Piwik\Session\SessionFingerprint $sessionFingerprint, $tsPasswordModified)
118 {
119 // sanity check, make sure users can still login if the ts_password_modified column does not exist
120 if ($tsPasswordModified === null) {
121 return \false;
122 }
123 // if the session start time doesn't exist for some reason, log the user out
124 $sessionStartTime = $sessionFingerprint->getSessionStartTime();
125 if (empty($sessionStartTime)) {
126 return \true;
127 }
128 return $sessionStartTime < Date::factory($tsPasswordModified)->getTimestampUTC();
129 }
130 private function makeAuthFailure()
131 {
132 return new AuthResult(AuthResult::FAILURE, null, null);
133 }
134 private function makeAuthSuccess($user,
135 #[\SensitiveParameter]
136 $tokenAuth)
137 {
138 $this->user = $user;
139 $this->tokenAuth = $tokenAuth;
140 $isSuperUser = (int) $user['superuser_access'];
141 $code = $isSuperUser ? AuthResult::SUCCESS_SUPERUSER_AUTH_CODE : AuthResult::SUCCESS;
142 return new AuthResult($code, $user['login'], $tokenAuth);
143 }
144 protected function initNewBlankSession(\Piwik\Session\SessionFingerprint $sessionFingerprint)
145 {
146 // this user should be using a different session, so generate a new ID
147 // NOTE: Zend_Session cannot be used since it will destroy the old
148 // session.
149 if ($this->shouldDestroySession) {
150 session_regenerate_id();
151 }
152 // regenerating the ID will create a new session w/ a new ID, but will
153 // copy over the existing session data. we want the new session for the
154 // unauthorized user to be different, so we clear the session fingerprint.
155 $sessionFingerprint->clear();
156 }
157 protected function destroyCurrentSession(\Piwik\Session\SessionFingerprint $sessionFingerprint)
158 {
159 // Note: Piwik will attempt to create another session in the LoginController
160 // when rendering the login form (the nonce for the form is stored in the session).
161 // So we can't use Session::destroy() since Zend prohibits starting a new session
162 // after session_destroy() is called. Instead we clear the session fingerprint for
163 // the existing session and generate a new session. Both the old session &
164 // new session should have no stored data.
165 $sessionFingerprint->clear();
166 if ($this->shouldDestroySession) {
167 Session::regenerateId();
168 }
169 }
170 public function getTokenAuth()
171 {
172 return $this->tokenAuth;
173 }
174 private function updateSessionExpireTime(\Piwik\Session\SessionFingerprint $sessionFingerprint)
175 {
176 $sessionParams = session_get_cookie_params();
177 // we update the session cookie to make sure expired session cookies are not available client side...
178 $sessionCookieLifetime = Config::getInstance()->General['login_cookie_expire'];
179 Session::writeCookie(session_name(), session_id(), time() + $sessionCookieLifetime, $sessionParams['path'], $sessionParams['domain'], $sessionParams['secure'], $sessionParams['httponly'], Session::getSameSiteCookieValue());
180 // ...and we also update the expiration time stored server side so we can prevent expired sessions from being reused
181 $sessionFingerprint->updateSessionExpirationTime();
182 }
183 private function isExpiredSession(\Piwik\Session\SessionFingerprint $sessionFingerprint)
184 {
185 $expirationTime = $sessionFingerprint->getExpirationTime();
186 if (empty($expirationTime)) {
187 return \true;
188 }
189 $isExpired = Date::now()->getTimestampUTC() > $expirationTime;
190 return $isExpired;
191 }
192 private function checkIfSessionFailedToRead()
193 {
194 if (Session\SaveHandler\DbTable::$wasSessionToLargeToRead) {
195 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.");
196 }
197 }
198 }
199