passwordResetter = $passwordResetter;
if (empty($auth)) {
$auth = StaticContainer::get('Piwik\Auth');
}
$this->auth = $auth;
if (empty($passwordVerify)) {
$passwordVerify = StaticContainer::get('Piwik\Plugins\Login\PasswordVerifier');
}
$this->passwordVerify = $passwordVerify;
if (empty($sessionInitializer)) {
$sessionInitializer = new \Piwik\Session\SessionInitializer();
}
$this->sessionInitializer = $sessionInitializer;
if (empty($bruteForceDetection)) {
$bruteForceDetection = StaticContainer::get('Piwik\Plugins\Login\Security\BruteForceDetection');
}
$this->bruteForceDetection = $bruteForceDetection;
if (empty($systemSettings)) {
$systemSettings = StaticContainer::get('Piwik\Plugins\Login\SystemSettings');
}
$this->systemSettings = $systemSettings;
}
/**
* Default action
*
* @return string
*/
function index()
{
return $this->login();
}
/**
* Login form
*
* @param string $messageNoAccess Access error message
* @param bool $infoMessage
* @internal param string $currentUrl Current URL
* @return string
*/
function login($messageNoAccess = null, $infoMessage = false)
{
$form = new FormLogin();
if ($form->validate()) {
$nonce = $form->getSubmitValue('form_nonce');
$messageNoAccess = Nonce::verifyNonceWithErrorMessage('Login.login', $nonce, null);
// validate if there is error message
if ($messageNoAccess === "") {
$loginOrEmail = $form->getSubmitValue('form_login');
$login = $this->getLoginFromLoginOrEmail($loginOrEmail);
$password = $form->getSubmitValue('form_password');
try {
$this->authenticateAndRedirect($login, $password);
} catch (Exception $e) {
$messageNoAccess = $e->getMessage();
}
}
}
if ($messageNoAccess) {
http_response_code(403);
}
$view = new View('@Login/login');
$view->AccessErrorString = $messageNoAccess;
$view->infoMessage = nl2br($infoMessage);
$view->addForm($form);
$this->configureView($view);
self::setHostValidationVariablesView($view);
return $view->render();
}
private function getLoginFromLoginOrEmail($loginOrEmail)
{
$model = new UsersModel();
if (!$model->userExists($loginOrEmail)) {
$user = $model->getUserByEmail($loginOrEmail);
if (!empty($user)) {
return $user['login'];
}
}
return $loginOrEmail;
}
/**
* Configure common view properties
*
* @param View $view
*/
protected function configureView($view)
{
$this->setBasicVariablesNoneAdminView($view);
$view->linkTitle = Piwik::getRandomTitle();
// crsf token: don't trust the submitted value; generate/fetch it from session data
$view->nonce = Nonce::getNonce('Login.login');
}
public function confirmPassword()
{
Piwik::checkUserIsNotAnonymous();
Piwik::checkUserHasSomeViewAccess();
if (!$this->passwordVerify->hasPasswordVerifyBeenRequested()) {
throw new Exception('Not available');
}
if (!Url::isValidHost()) {
throw new Exception("Cannot confirm password with untrusted hostname!");
}
$nonceKey = 'confirmPassword';
$messageNoAccess = '';
if (!empty($_POST)) {
$nonce = Common::getRequestVar('nonce', null, 'string', $_POST);
$password = Common::getRequestVar('password', null, 'string', $_POST);
if ($password) {
$password = Common::unsanitizeInputValue($password);
}
$errorMessage = Nonce::verifyNonceWithErrorMessage($nonceKey, $nonce);
if ($errorMessage !== "") {
$messageNoAccess = $errorMessage;
} elseif ($this->passwordVerify->isPasswordCorrect(Piwik::getCurrentUserLogin(), $password)) {
$this->passwordVerify->setPasswordVerifiedCorrectly();
return;
} else {
$messageNoAccess = Piwik::translate('Login_WrongPasswordEntered');
}
}
return $this->renderTemplate('@Login/confirmPassword', array(
'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
* @throws Exception
* @return void
*/
function logme()
{
if (Config::getInstance()->General['login_allow_logme'] == 0) {
throw new Exception('This functionality has been disabled in config');
}
$password = Common::getRequestVar('password', null, 'string');
$login = Common::getRequestVar('login', null, 'string');
if (Piwik::hasTheUserSuperUserAccess($login)) {
throw new Exception(Piwik::translate('Login_ExceptionInvalidSuperUserAccessAuthenticationMethod', array("logme")));
}
$currentUrl = 'index.php';
if ($this->idSite) {
$currentUrl .= '?idSite=' . $this->idSite;
}
$urlToRedirect = Common::getRequestVar('url', $currentUrl, 'string');
$urlToRedirect = Common::unsanitizeInputValue($urlToRedirect);
$this->authenticateAndRedirect($login, $password, $urlToRedirect, $passwordHashed = true);
}
public function bruteForceLog()
{
Piwik::checkUserHasSuperUserAccess();
return $this->renderTemplate('bruteForceLog', array(
'blockedIps' => $this->bruteForceDetection->getCurrentlyBlockedIps(),
'blacklistedIps' => $this->systemSettings->blacklistedBruteForceIps->getValue()
));
}
/**
* Error message shown when an AJAX request has no access
*
* @param string $errorMessage
* @return string
*/
public function ajaxNoAccess($errorMessage)
{
return sprintf(
'
',
Piwik::translate('General_Error'),
htmlentities($errorMessage, Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8', $doubleEncode = false),
'index.php?module=' . Piwik::getLoginPluginName(),
Piwik::translate('Login_LogIn')
);
}
/**
* Authenticate user and password. Redirect if successful.
*
* @param string $login user name
* @param string $password plain-text or hashed password
* @param string $urlToRedirect URL to redirect to, if successfully authenticated
* @param bool $passwordHashed indicates if $password is hashed
* @return string failure message if unable to authenticate
*/
protected function authenticateAndRedirect($login, $password, $urlToRedirect = false, $passwordHashed = false)
{
Nonce::discardNonce('Login.login');
$this->auth->setLogin($login);
if ($passwordHashed === false) {
$this->auth->setPassword($password);
} else {
$this->auth->setPasswordHash($password);
}
$this->sessionInitializer->initSession($this->auth);
// remove password reset entry if it exists
$this->passwordResetter->removePasswordResetInfo($login);
$parsedUrl = parse_url($urlToRedirect);
// only use redirect url if host is trusted
if (!empty($parsedUrl['host']) && !Url::isValidHost($parsedUrl['host'])) {
$e = new \Piwik\Exception\Exception('The redirect URL host is not valid, it is not a trusted host. If this URL is trusted, you can allow this in your config.ini.php file by adding the line trusted_hosts[] = "'.Common::sanitizeInputValue($parsedUrl['host']).'" under [General]');
$e->setIsHtmlMessage();
throw $e;
}
if (empty($urlToRedirect)) {
$redirect = Common::unsanitizeInputValue(Common::getRequestVar('form_redirect', false));
$redirectParams = UrlHelper::getArrayFromQueryString(UrlHelper::getQueryFromUrl($redirect));
$module = Common::getRequestVar('module', '', 'string', $redirectParams);
// when module is login, we redirect to home...
if (!empty($module) && $module !== 'Login' && $module !== Piwik::getLoginPluginName() && $redirect) {
$host = Url::getHostFromUrl($redirect);
$currentHost = Url::getHost();
$currentHost = explode(':', $currentHost, 2)[0];
// we only redirect to a trusted host
if (!empty($host) && !empty($currentHost) && $host == $currentHost && Url::isValidHost($host)
) {
$urlToRedirect = $redirect;
}
}
}
if (empty($urlToRedirect)) {
$urlToRedirect = Url::getCurrentUrlWithoutQueryString();
}
Url::redirectToUrl($urlToRedirect);
}
/**
* Reset password action. Stores new password as hash and sends email
* to confirm use.
*
*/
function resetPassword()
{
$infoMessage = null;
$formErrors = null;
$form = new FormResetPassword();
if ($form->validate()) {
$nonce = $form->getSubmitValue('form_nonce');
$errorMessage = Nonce::verifyNonceWithErrorMessage('Login.login', $nonce);
if ($errorMessage === "") {
$formErrors = $this->resetPasswordFirstStep($form);
if (empty($formErrors)) {
$infoMessage = Piwik::translate('Login_ConfirmationLinkSent');
}
} else {
$formErrors = array($errorMessage);
}
} else {
// if invalid, display error
$formData = $form->getFormData();
$formErrors = $formData['errors'];
}
$view = new View('@Login/resetPassword');
$view->infoMessage = $infoMessage;
$view->formErrors = $formErrors;
return $view->render();
}
/**
* Saves password reset info and sends confirmation email.
*
* @param QuickForm2 $form
* @return array Error message(s) if an error occurs.
*/
protected function resetPasswordFirstStep($form)
{
$loginMail = $form->getSubmitValue('form_login');
$password = $form->getSubmitValue('form_password');
try {
$this->passwordResetter->initiatePasswordResetProcess($loginMail, $password);
} catch (Exception $ex) {
Log::debug($ex);
return array($ex->getMessage());
}
return null;
}
/**
* Password reset confirmation action. Finishes the password reset process.
* Users visit this action from a link supplied in an email.
*/
public function confirmResetPassword()
{
if (!Url::isValidHost()) {
throw new Exception("Cannot confirm reset password with untrusted hostname!");
}
$errorMessage = null;
$passwordHash = null;
$login = Common::getRequestVar('login');
$resetToken = Common::getRequestVar('resetToken');
try {
$passwordHash = $this->passwordResetter->checkValidConfirmPasswordToken($login, $resetToken);
} catch (Exception $ex) {
Log::debug($ex);
$errorMessage = $ex->getMessage();
}
if (!empty($errorMessage)) {
return $this->login($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)) {
$this->passwordResetter->setHashedPasswordForLogin($login, $passwordHash);
return $this->resetPasswordSuccess();
} else {
$errorMessage = Piwik::translate('Login_ConfirmPasswordResetWrongPassword');
}
}
$nonce = Nonce::getNonce(self::NONCE_CONFIRMRESETPASSWORD);
return $this->renderTemplateAs('confirmResetPassword', array(
'nonce' => $nonce,
'errorMessage' => $errorMessage
), 'basic');
}
/**
* The action used after a password is successfully reset. Displays the login
* screen with an extra message. A separate action is used instead of returning
* the HTML in confirmResetPassword so the resetToken won't be in the URL.
*/
public function resetPasswordSuccess()
{
$_POST = array(); // prevent showing error message username and password is missing
return $this->login($errorMessage = null, $infoMessage = Piwik::translate('Login_PasswordChanged'));
}
/**
* Clear session information
*
* @return void
*/
public static function clearSession()
{
$sessionFingerprint = new Session\SessionFingerprint();
$sessionFingerprint->clear();
Session::expireSessionCookie();
}
/**
* Logout current user
*
* @return void
*/
public function logout()
{
Piwik::postEvent('Login.logout', array(Piwik::getCurrentUserLogin()));
self::clearSession();
$logoutUrl = @Config::getInstance()->General['login_logout_url'];
if (empty($logoutUrl)) {
Piwik::redirectToModule('CoreHome');
} else {
Url::redirectToUrl($logoutUrl);
}
}
}