PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.3.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.3.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 5 years ago SessionAuth.php 5 years ago SessionFingerprint.php 5 years ago SessionInitializer.php 5 years ago SessionNamespace.php 5 years ago
SessionAuth.php
235 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\Date;
17 use Piwik\Plugins\UsersManager\Model;
18 use Piwik\Plugins\UsersManager\Model as UsersModel;
19 use Piwik\Session;
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 if ($this->isExpiredSession($sessionFingerprint)) {
98 $sessionFingerprint->clear();
99 return $this->makeAuthFailure();
100 }
101
102 $userForSession = $sessionFingerprint->getUser();
103 if (empty($userForSession)) {
104 return $this->makeAuthFailure();
105 }
106
107 $user = $userModel->getUser($userForSession);
108 if (empty($user)
109 || $user['login'] !== $userForSession // sanity check in case there's a bug in getUser()
110 ) {
111 return $this->makeAuthFailure();
112 }
113
114 $tsPasswordModified = !empty($user['ts_password_modified']) ? $user['ts_password_modified'] : null;
115 if ($this->isSessionStartedBeforePasswordChange($sessionFingerprint, $tsPasswordModified)) {
116 $this->destroyCurrentSession($sessionFingerprint);
117 return $this->makeAuthFailure();
118 }
119
120 $this->updateSessionExpireTime($sessionFingerprint);
121
122 if ($this->tokenAuth !== null
123 && $this->tokenAuth !== false
124 && $this->tokenAuth !== $sessionFingerprint->getSessionTokenAuth()) {
125 return $this->makeAuthFailure();
126 }
127
128 if ($sessionFingerprint->getSessionTokenAuth()) {
129 $tokenAuth = $sessionFingerprint->getSessionTokenAuth();
130 } else {
131 $tokenAuth = $this->userModel->generateRandomTokenAuth();
132 }
133
134 return $this->makeAuthSuccess($user, $tokenAuth);
135 }
136
137 private function isSessionStartedBeforePasswordChange(SessionFingerprint $sessionFingerprint, $tsPasswordModified)
138 {
139 // sanity check, make sure users can still login if the ts_password_modified column does not exist
140 if ($tsPasswordModified === null) {
141 return false;
142 }
143
144 // if the session start time doesn't exist for some reason, log the user out
145 $sessionStartTime = $sessionFingerprint->getSessionStartTime();
146 if (empty($sessionStartTime)) {
147 return true;
148 }
149
150 return $sessionStartTime < Date::factory($tsPasswordModified)->getTimestampUTC();
151 }
152
153 private function makeAuthFailure()
154 {
155 return new AuthResult(AuthResult::FAILURE, null, null);
156 }
157
158 private function makeAuthSuccess($user, $tokenAuth)
159 {
160 $this->user = $user;
161 $this->tokenAuth = $tokenAuth;
162
163 $isSuperUser = (int) $user['superuser_access'];
164 $code = $isSuperUser ? AuthResult::SUCCESS_SUPERUSER_AUTH_CODE : AuthResult::SUCCESS;
165
166 return new AuthResult($code, $user['login'], $tokenAuth);
167 }
168
169 protected function initNewBlankSession(SessionFingerprint $sessionFingerprint)
170 {
171 // this user should be using a different session, so generate a new ID
172 // NOTE: Zend_Session cannot be used since it will destroy the old
173 // session.
174 if ($this->shouldDestroySession) {
175 session_regenerate_id();
176 }
177
178 // regenerating the ID will create a new session w/ a new ID, but will
179 // copy over the existing session data. we want the new session for the
180 // unauthorized user to be different, so we clear the session fingerprint.
181 $sessionFingerprint->clear();
182 }
183
184 protected function destroyCurrentSession(SessionFingerprint $sessionFingerprint)
185 {
186 // Note: Piwik will attempt to create another session in the LoginController
187 // when rendering the login form (the nonce for the form is stored in the session).
188 // So we can't use Session::destroy() since Zend prohibits starting a new session
189 // after session_destroy() is called. Instead we clear the session fingerprint for
190 // the existing session and generate a new session. Both the old session &
191 // new session should have no stored data.
192 $sessionFingerprint->clear();
193 if ($this->shouldDestroySession) {
194 Session::regenerateId();
195 }
196 }
197
198 public function getTokenAuth()
199 {
200 return $this->tokenAuth;
201 }
202
203 private function updateSessionExpireTime(SessionFingerprint $sessionFingerprint)
204 {
205 $sessionParams = session_get_cookie_params();
206
207 // we update the session cookie to make sure expired session cookies are not available client side...
208 $sessionCookieLifetime = Config::getInstance()->General['login_cookie_expire'];
209 Session::writeCookie(
210 session_name(),
211 session_id(),
212 time() + $sessionCookieLifetime,
213 $sessionParams['path'],
214 $sessionParams['domain'],
215 $sessionParams['secure'],
216 $sessionParams['httponly'],
217 Session::getSameSiteCookieValue()
218 );
219
220 // ...and we also update the expiration time stored server side so we can prevent expired sessions from being reused
221 $sessionFingerprint->updateSessionExpirationTime();
222 }
223
224 private function isExpiredSession(SessionFingerprint $sessionFingerprint)
225 {
226 $expirationTime = $sessionFingerprint->getExpirationTime();
227 if (empty($expirationTime)) {
228 return true;
229 }
230
231 $isExpired = Date::now()->getTimestampUTC() > $expirationTime;
232 return $isExpired;
233 }
234 }
235