diff options
Diffstat (limited to 'plugins/Login/Controller.php')
-rw-r--r-- | plugins/Login/Controller.php | 257 |
1 files changed, 151 insertions, 106 deletions
diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php index 98b0dd23e9..08c657827a 100644 --- a/plugins/Login/Controller.php +++ b/plugins/Login/Controller.php @@ -1,4 +1,5 @@ <?php + /** * Matomo - free/libre analytics platform * @@ -6,6 +7,7 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * */ + namespace Piwik\Plugins\Login; use Exception; @@ -13,12 +15,14 @@ use Piwik\Auth\Password; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; +use Piwik\Date; use Piwik\Log; use Piwik\Nonce; use Piwik\Piwik; use Piwik\Plugins\CoreAdminHome\Emails\UserAcceptInvitationEmail; use Piwik\Plugins\CoreAdminHome\Emails\UserDeclinedInvitationEmail; use Piwik\Plugins\Login\Security\BruteForceDetection; +use Piwik\Plugins\PrivacyManager\SystemSettings; use Piwik\Plugins\UsersManager\Model as UsersModel; use Piwik\Plugins\UsersManager\UsersManager; use Piwik\QuickForm2; @@ -70,19 +74,19 @@ class Controller extends \Piwik\Plugin\ControllerAdmin * Constructor. * * @param PasswordResetter $passwordResetter - * @param AuthInterface $auth + * @param \Piwik\Auth $auth * @param SessionInitializer $sessionInitializer * @param PasswordVerifier $passwordVerify * @param BruteForceDetection $bruteForceDetection * @param SystemSettings $systemSettings */ public function __construct( - $passwordResetter = null, - $auth = null, - $sessionInitializer = null, - $passwordVerify = null, - $bruteForceDetection = null, - $systemSettings = null + $passwordResetter = null, + $auth = null, + $sessionInitializer = null, + $passwordVerify = null, + $bruteForceDetection = null, + $systemSettings = null ) { parent::__construct(); @@ -122,7 +126,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin * * @return string */ - function index() + public function index() { return $this->login(); } @@ -135,7 +139,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin * @return string * @internal param string $currentUrl Current URL */ - function login($messageNoAccess = null, $infoMessage = false) + public function login($messageNoAccess = null, $infoMessage = false) { $form = new FormLogin(); if ($form->validate()) { @@ -231,20 +235,20 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } } - return $this->renderTemplate('@Login/confirmPassword', array( + return $this->renderTemplate('@Login/confirmPassword', [ 'nonce' => Nonce::getNonce($nonceKey), 'AccessErrorString' => $messageNoAccess, 'loginPlugin' => Piwik::getLoginPluginName(), - )); + ]); } /** * Form-less login - * @see how to use it on http://piwik.org/faq/how-to/#faq_30 + * @see how to use it on https://matomo.org/faq/how-to/faq_30 * @throws Exception * @return void */ - function logme() + public function logme() { if (Config::getInstance()->General['login_allow_logme'] == 0) { throw new Exception('This functionality has been disabled in config'); @@ -254,8 +258,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $login = Common::getRequestVar('login', null, 'string'); if (Piwik::hasTheUserSuperUserAccess($login)) { - throw new Exception(Piwik::translate('Login_ExceptionInvalidSuperUserAccessAuthenticationMethod', - array("logme"))); + throw new Exception( + Piwik::translate('Login_ExceptionInvalidSuperUserAccessAuthenticationMethod', ["logme"]) + ); } $currentUrl = 'index.php'; @@ -274,10 +279,10 @@ class Controller extends \Piwik\Plugin\ControllerAdmin { Piwik::checkUserHasSuperUserAccess(); - return $this->renderTemplate('bruteForceLog', array( + return $this->renderTemplate('bruteForceLog', [ 'blockedIps' => $this->bruteForceDetection->getCurrentlyBlockedIps(), 'blacklistedIps' => $this->systemSettings->blacklistedBruteForceIps->getValue() - )); + ]); } /** @@ -289,14 +294,14 @@ class Controller extends \Piwik\Plugin\ControllerAdmin public function ajaxNoAccess($errorMessage) { return sprintf( - '<div class="alert alert-danger"> + '<div class="alert alert-danger"> <p><strong>%s:</strong> %s</p> <p><a href="%s">%s</a></p> </div>', - Piwik::translate('General_Error'), - htmlentities($errorMessage, Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8', $doubleEncode = false), - 'index.php?module=' . Piwik::getLoginPluginName(), - Piwik::translate('Login_LogIn') + Piwik::translate('General_Error'), + htmlentities($errorMessage, Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8', $doubleEncode = false), + 'index.php?module=' . Piwik::getLoginPluginName(), + Piwik::translate('Login_LogIn') ); } @@ -345,7 +350,8 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $currentHost = explode(':', $currentHost, 2)[0]; // we only redirect to a trusted host - if (!empty($host) && !empty($currentHost) && $host == $currentHost && Url::isValidHost($host) + if ( + !empty($host) && !empty($currentHost) && $host == $currentHost && Url::isValidHost($host) ) { $urlToRedirect = $redirect; } @@ -362,9 +368,8 @@ class Controller extends \Piwik\Plugin\ControllerAdmin /** * Reset password action. Stores new password as hash and sends email * to confirm use. - * */ - function resetPassword() + public function resetPassword() { $infoMessage = null; $formErrors = null; @@ -379,7 +384,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $infoMessage = Piwik::translate('Login_ConfirmationLinkSent'); } } else { - $formErrors = array($errorMessage); + $formErrors = [$errorMessage]; } } else { // if invalid, display error @@ -410,7 +415,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin } catch (Exception $ex) { Log::debug($ex); - return array($ex->getMessage()); + return [$ex->getMessage()]; } return null; @@ -444,15 +449,21 @@ class Controller extends \Piwik\Plugin\ControllerAdmin return $this->login($errorMessage); } - if (!empty($_POST['nonce']) - && !empty($_POST['mtmpasswordconfirm']) - && !empty($resetToken) - && !empty($login) - && !empty($passwordHash) - && empty($errorMessage)) { + if ( + !empty($_POST['nonce']) + && !empty($_POST['mtmpasswordconfirm']) + && !empty($resetToken) + && !empty($login) + && !empty($passwordHash) + && empty($errorMessage) + ) { Nonce::checkNonce(self::NONCE_CONFIRMRESETPASSWORD, $_POST['nonce']); - if ($this->passwordResetter->doesResetPasswordHashMatchesPassword($_POST['mtmpasswordconfirm'], - $passwordHash)) { + if ( + $this->passwordResetter->doesResetPasswordHashMatchesPassword( + $_POST['mtmpasswordconfirm'], + $passwordHash + ) + ) { $this->passwordResetter->setHashedPasswordForLogin($login, $passwordHash); return $this->resetPasswordSuccess(); } else { @@ -462,10 +473,10 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $nonce = Nonce::getNonce(self::NONCE_CONFIRMRESETPASSWORD); - return $this->renderTemplateAs('confirmResetPassword', array( + return $this->renderTemplateAs('confirmResetPassword', [ 'nonce' => $nonce, 'errorMessage' => $errorMessage - ), 'basic'); + ], 'basic'); } /** @@ -475,7 +486,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin */ public function resetPasswordSuccess() { - $_POST = array(); // prevent showing error message username and password is missing + $_POST = []; // prevent showing error message username and password is missing return $this->login($errorMessage = null, $infoMessage = Piwik::translate('Login_PasswordChanged')); } @@ -499,7 +510,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin */ public function logout() { - Piwik::postEvent('Login.logout', array(Piwik::getCurrentUserLogin())); + Piwik::postEvent('Login.logout', [Piwik::getCurrentUserLogin()]); self::clearSession(); @@ -524,79 +535,99 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $token = Common::getRequestVar('token', null, 'string'); $form = Common::getRequestVar('invitation_form', false, 'string'); - //check token is valid - $user = $model->getUserByTokenAuth($token); - if ($user['invite_status'] !== 'pending') { - throw new Exception(Piwik::translate('Login_InvalidOrExpiredToken')); - } + $settings = new SystemSettings(); + $termsAndConditionUrl = $settings->termsAndConditionUrl->getValue(); + $privacyPolicyUrl = $settings->privacyPolicyUrl->getValue(); + $user = $model->getUserByInviteToken($token); - //if user not match the invite user + // if no user matches the invite token if (!$user) { throw new Exception(Piwik::translate('Login_InvalidUsernameEmail')); } - //if form is blank + if (!empty($user['invite_expired_at']) && Date::factory($user['invite_expired_at'])->isEarlier(Date::now())) { + throw new Exception(Piwik::translate('Login_InvalidOrExpiredToken')); + } + + // if form was sent if (!empty($form)) { $error = null; $password = Common::getRequestVar('password', false, 'string'); $passwordConfirmation = Common::getRequestVar('passwordConfirmation', false, 'string'); - $terms = Common::getRequestVar('terms', false, 'string'); + $conditionCheck = Common::getRequestVar('conditionCheck', false, 'string'); + if (!$password) { $error = Piwik::translate('Login_PasswordRequired'); } - //not accept terms - if (!$terms) { - $error = Piwik::translate('Login_TermsRequired'); + // check if terms accepted and privacy + if (!$conditionCheck && ($privacyPolicyUrl || $termsAndConditionUrl)) { + if ($privacyPolicyUrl && $termsAndConditionUrl) { + $error = Piwik::translate('Login_AcceptPrivacyPolicyAndTermsAndCondition'); + } elseif ($privacyPolicyUrl) { + $error = Piwik::translate('Login_AcceptPrivacyPolicy'); + } elseif ($termsAndConditionUrl) { + $error = Piwik::translate('Login_AcceptTermsAndCondition'); + } } - //valid password + // validate password if (!UsersManager::isValidPasswordString($password)) { - $error = Piwik::translate('UsersManager_ExceptionInvalidPassword', - array(UsersManager::PASSWORD_MIN_LENGTH)); + $error = Piwik::translate('UsersManager_ExceptionInvalidPassword', [UsersManager::PASSWORD_MIN_LENGTH]); } - //confirm matching password + + // confirm matching passwords if ($password !== $passwordConfirmation) { $error = Piwik::translate('Login_PasswordsDoNotMatch'); } if (!$error) { $password = UsersManager::getPasswordHash($password); - $passwordInfo = $passwordHelper->info($password); - - if (!isset($passwordInfo['algo']) || 0 >= $passwordInfo['algo']) { - // password may have already been fully hashed - $password = $passwordHelper->hash($password); + $password = $passwordHelper->hash($password); + + // update pending user to active user + $model->updateUserFields( + $user['login'], + [ + 'password' => $password, + 'invite_token' => null, + 'invite_accept_at' => Date::now()->getDatetime(), + 'invite_expired_at' => null, + ] + ); + + // send e-mail to inviter + if (!empty($user['invited_by'])) { + $invitedBy = $model->getUser($user['invited_by']); + if ($invitedBy) { + $mail = StaticContainer::getContainer()->make(UserAcceptInvitationEmail::class, [ + 'login' => $user['invited_by'], + 'emailAddress' => $invitedBy['email'], + 'userLogin' => $user['login'], + ]); + $mail->safeSend(); + } } - //update pending user to active user - $model->updateUserFields($user['login'], ['password' => $password, 'invite_status' => 'accept']); - $sessionInitializer = new SessionInitializer(); - $auth = StaticContainer::get('Piwik\Auth'); - $auth->setTokenAuth(null); // ensure authenticated through password - $auth->setLogin($user['login']); - $auth->setPassword($passwordConfirmation); - $sessionInitializer->initSession($auth); - - //send Admin Email - try { - $mail = StaticContainer::getContainer()->make(UserAcceptInvitationEmail::class, array( - 'login' => $user['login'], - 'emailAddress' => $user['email'], - 'userLogin' => $user['login'], - )); - $mail->safeSend(); - } catch (\Exception $e) { - - } + /** + * Triggered after a user accepted an invite + * + * @param string $userLogin The invited user's login. + * @param string $email The invited user's e-mail. + * @param string $inviterLogin The login of the user, who invited this user + */ + Piwik::postEvent('UsersManager.inviteUser.accepted', [$user['login'], $user['email'], $user['invited_by']]); - $this->redirectToIndex('CoreHome', 'index'); + $this->authenticateAndRedirect($user['login'], $passwordConfirmation); } + $view->AccessErrorString = $error; } + $view->user = $user; + $view->termsAndCondition = $termsAndConditionUrl; + $view->privacyPolicyUrl = $privacyPolicyUrl; $view->token = $token; - $view->declined = false; $this->configureView($view); self::setHostValidationVariablesView($view); return $view->render(); @@ -609,42 +640,56 @@ class Controller extends \Piwik\Plugin\ControllerAdmin $token = Common::getRequestVar('token', null, 'string'); $form = Common::getRequestVar('invitation_form', false, 'string'); - $user = $model->getUserByTokenAuth($token); - if ($user['invite_status'] !== 'pending') { + $user = $model->getUserByInviteToken($token); + + // if no user matches the invite token + if (!$user) { throw new Exception(Piwik::translate('Login_InvalidOrExpiredToken')); } - //if user not match the invite user - if (!$user) { - throw new Exception(Piwik::translate('Login_InvalidUsernameEmail')); + + if (!empty($user['invite_expired_at']) && Date::factory($user['invite_expired_at'])->isEarlier(Date::now())) { + throw new Exception(Piwik::translate('Login_InvalidOrExpiredToken')); } - if ($form) { - $model->deleteAllTokensForUser($user['login']); - $model->updateUserFields($user['login'], ['invite_status' => 'decline']); - $this->redirectToIndex('Login', 'index'); + $view = new View('@Login/invitationDecline'); - } + if ($form) { + // remove user + try { + $model->deleteUser($user['login']); + } catch (\Exception $e) { + // deleting the user triggers an event, which might call methods that require a user to be logged in + // as those operations might not be needed for a pending user, we simply ignore any errors here + } - $view = new View('@Login/invitation'); - $view->declined = true; - $view->token = $token; + // send e-mail to inviter + if (!empty($user['invited_by'])) { + $invitedBy = $model->getUser($user['invited_by']); + if ($invitedBy) { + $mail = StaticContainer::getContainer()->make(UserDeclinedInvitationEmail::class, [ + 'login' => $user['invited_by'], + 'emailAddress' => $invitedBy['email'], + 'userLogin' => $user['login'], + ]); + $mail->safeSend(); + } + } - //send Admin Email - try { - $mail = StaticContainer::getContainer()->make(UserDeclinedInvitationEmail::class, array( - 'login' => $user['login'], - 'emailAddress' => $user['email'], - 'userLogin' => $user['login'], - )); - $mail->safeSend(); - } catch (\Exception $e) { + $view = new View('@Login/invitationDeclineSuccess'); + /** + * Triggered after a user accepted an invite + * + * @param string $userLogin The invited user's login. + * @param string $email The invited user's e-mail. + * @param string $inviterLogin The login of the user, who invited this user + */ + Piwik::postEvent('UsersManager.inviteUser.declined', [$user['login'], $user['email'], $user['invited_by']]); } + + $view->token = $token; $this->configureView($view); self::setHostValidationVariablesView($view); return $view->render(); - - } - } |