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 */ 9namespace Piwik\Plugins\Login; 10 11use Piwik\AuthResult; 12use Piwik\Auth\Password; 13use Piwik\Common; 14use Piwik\Date; 15use Piwik\DbHelper; 16use Piwik\Piwik; 17use Piwik\Plugins\UsersManager\Model; 18use Piwik\Plugins\UsersManager\UsersManager; 19 20class Auth implements \Piwik\Auth 21{ 22 protected $login; 23 protected $token_auth; 24 protected $hashedPassword; 25 26 /** 27 * @var Model 28 */ 29 private $userModel; 30 31 /** 32 * @var Password 33 */ 34 private $passwordHelper; 35 36 public function __construct() 37 { 38 $this->userModel = new Model(); 39 $this->passwordHelper = new Password(); 40 } 41 42 /** 43 * Authentication module's name, e.g., "Login" 44 * 45 * @return string 46 */ 47 public function getName() 48 { 49 return 'Login'; 50 } 51 52 /** 53 * Authenticates user 54 * 55 * @return AuthResult 56 */ 57 public function authenticate() 58 { 59 try { 60 if (!empty($this->hashedPassword)) { 61 return $this->authenticateWithPassword($this->login, $this->getTokenAuthSecret()); 62 } elseif (is_null($this->login)) { 63 return $this->authenticateWithToken($this->token_auth); 64 } elseif (!empty($this->login)) { 65 return $this->authenticateWithLoginAndToken($this->token_auth, $this->login); 66 } 67 } catch (\Zend_Db_Statement_Exception $e) { 68 // user_token_auth table might not yet exist when updating to Matomo 4 69 if (strpos($e->getMessage(), 'user_token_auth') && !DbHelper::tableExists(Common::prefixTable('user_token_auth'))) { 70 return new AuthResult(AuthResult::SUCCESS, 'anonymous', 'anonymous'); 71 } 72 73 throw $e; 74 } 75 76 return new AuthResult(AuthResult::FAILURE, $this->login, $this->token_auth); 77 } 78 79 private function authenticateWithPassword($login, $passwordHash) 80 { 81 $user = $this->userModel->getUser($login); 82 83 if (empty($user['login'])) { 84 return new AuthResult(AuthResult::FAILURE, $login, null); 85 } 86 87 if ($this->passwordHelper->verify($passwordHash, $user['password'])) { 88 if ($this->passwordHelper->needsRehash($user['password'])) { 89 $newPasswordHash = $this->passwordHelper->hash($passwordHash); 90 91 $this->userModel->updateUser($login, $newPasswordHash, $user['email']); 92 } 93 $this->token_auth = null; // make sure to generate a random token 94 95 return $this->authenticationSuccess($user); 96 } 97 98 return new AuthResult(AuthResult::FAILURE, $login, null); 99 } 100 101 private function authenticateWithToken($token) 102 { 103 $user = $this->userModel->getUserByTokenAuth($token); 104 105 if (!empty($user['login'])) { 106 $this->userModel->setTokenAuthWasUsed($token, Date::now()->getDatetime()); 107 return $this->authenticationSuccess($user); 108 } 109 110 return new AuthResult(AuthResult::FAILURE, null, $token); 111 } 112 113 private function authenticateWithLoginAndToken($token, $login) 114 { 115 $user = $this->userModel->getUserByTokenAuth($token); 116 117 if (!empty($user['login']) && $user['login'] === $login) { 118 $this->userModel->setTokenAuthWasUsed($token, Date::now()->getDatetime()); 119 return $this->authenticationSuccess($user); 120 } 121 122 return new AuthResult(AuthResult::FAILURE, $login, $token); 123 } 124 125 private function authenticationSuccess(array $user) 126 { 127 if (empty($this->token_auth)) { 128 $this->token_auth = $this->userModel->generateRandomTokenAuth(); 129 // we generated one randomly which will then be stored in the session and used across the session 130 } 131 132 $isSuperUser = (int) $user['superuser_access']; 133 $code = $isSuperUser ? AuthResult::SUCCESS_SUPERUSER_AUTH_CODE : AuthResult::SUCCESS; 134 135 return new AuthResult($code, $user['login'], $this->token_auth); 136 } 137 138 /** 139 * Returns the login of the user being authenticated. 140 * 141 * @return string 142 */ 143 public function getLogin() 144 { 145 return $this->login; 146 } 147 148 /** 149 * Accessor to set login name 150 * 151 * @param string $login user login 152 */ 153 public function setLogin($login) 154 { 155 $this->login = $login; 156 } 157 158 /** 159 * Returns the secret used to calculate a user's token auth. 160 * 161 * @return string 162 */ 163 public function getTokenAuthSecret() 164 { 165 return $this->hashedPassword; 166 } 167 168 /** 169 * Accessor to set authentication token 170 * 171 * @param string $token_auth authentication token 172 */ 173 public function setTokenAuth($token_auth) 174 { 175 $this->token_auth = $token_auth; 176 } 177 178 /** 179 * Sets the password to authenticate with. 180 * 181 * @param string $password 182 */ 183 public function setPassword($password) 184 { 185 if (empty($password)) { 186 $this->hashedPassword = null; 187 } else { 188 $this->hashedPassword = UsersManager::getPasswordHash($password); 189 } 190 } 191 192 /** 193 * Sets the password hash to use when authentication. 194 * 195 * @param string $passwordHash The password hash. 196 */ 197 public function setPasswordHash($passwordHash) 198 { 199 if ($passwordHash === null) { 200 $this->hashedPassword = null; 201 return; 202 } 203 204 // check that the password hash is valid (sanity check) 205 UsersManager::checkPasswordHash($passwordHash, Piwik::translate('Login_ExceptionPasswordMD5HashExpected')); 206 207 $this->hashedPassword = $passwordHash; 208 } 209 210 // for tests 211 public function getTokenAuth() 212 { 213 return $this->token_auth; 214 } 215} 216