PageRenderTime 23ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/piwik/plugins/Login/Controller.php

https://github.com/imagesdesmaths/idm
PHP | 334 lines | 187 code | 46 blank | 101 comment | 21 complexity | 0bc57bf9ce45f0ff791885ade33fd217 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, BSD-2-Clause, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\Login;
  10. use Exception;
  11. use Piwik\Access;
  12. use Piwik\Auth as AuthInterface;
  13. use Piwik\Common;
  14. use Piwik\Config;
  15. use Piwik\Cookie;
  16. use Piwik\Log;
  17. use Piwik\Nonce;
  18. use Piwik\Piwik;
  19. use Piwik\QuickForm2;
  20. use Piwik\Session;
  21. use Piwik\Url;
  22. use Piwik\View;
  23. /**
  24. * Login controller
  25. *
  26. */
  27. class Controller extends \Piwik\Plugin\Controller
  28. {
  29. /**
  30. * @var PasswordResetter
  31. */
  32. private $passwordResetter;
  33. /**
  34. * @var Auth
  35. */
  36. private $auth;
  37. /**
  38. * @var SessionInitializer
  39. */
  40. private $sessionInitializer;
  41. /**
  42. * Constructor.
  43. *
  44. * @param PasswordResetter $passwordResetter
  45. * @param AuthInterface $auth
  46. * @param SessionInitializer $authenticatedSessionFactory
  47. \ */
  48. public function __construct($passwordResetter = null, $auth = null, $sessionInitializer = null)
  49. {
  50. parent::__construct();
  51. if (empty($passwordResetter)) {
  52. $passwordResetter = new PasswordResetter();
  53. }
  54. $this->passwordResetter = $passwordResetter;
  55. if (empty($auth)) {
  56. $auth = \Piwik\Registry::get('auth');
  57. }
  58. $this->auth = $auth;
  59. if (empty($sessionInitializer)) {
  60. $sessionInitializer = new SessionInitializer();
  61. }
  62. $this->sessionInitializer = $sessionInitializer;
  63. }
  64. /**
  65. * Default action
  66. *
  67. * @param none
  68. * @return string
  69. */
  70. function index()
  71. {
  72. return $this->login();
  73. }
  74. /**
  75. * Login form
  76. *
  77. * @param string $messageNoAccess Access error message
  78. * @param bool $infoMessage
  79. * @internal param string $currentUrl Current URL
  80. * @return string
  81. */
  82. function login($messageNoAccess = null, $infoMessage = false)
  83. {
  84. $form = new FormLogin();
  85. if ($form->validate()) {
  86. $nonce = $form->getSubmitValue('form_nonce');
  87. if (Nonce::verifyNonce('Login.login', $nonce)) {
  88. $login = $form->getSubmitValue('form_login');
  89. $password = $form->getSubmitValue('form_password');
  90. $rememberMe = $form->getSubmitValue('form_rememberme') == '1';
  91. try {
  92. $this->authenticateAndRedirect($login, $password, $rememberMe);
  93. } catch (Exception $e) {
  94. $messageNoAccess = $e->getMessage();
  95. }
  96. } else {
  97. $messageNoAccess = $this->getMessageExceptionNoAccess();
  98. }
  99. }
  100. $view = new View('@Login/login');
  101. $view->AccessErrorString = $messageNoAccess;
  102. $view->infoMessage = nl2br($infoMessage);
  103. $view->addForm($form);
  104. $this->configureView($view);
  105. self::setHostValidationVariablesView($view);
  106. return $view->render();
  107. }
  108. /**
  109. * Configure common view properties
  110. *
  111. * @param View $view
  112. */
  113. private function configureView($view)
  114. {
  115. $this->setBasicVariablesView($view);
  116. $view->linkTitle = Piwik::getRandomTitle();
  117. // crsf token: don't trust the submitted value; generate/fetch it from session data
  118. $view->nonce = Nonce::getNonce('Login.login');
  119. }
  120. /**
  121. * Form-less login
  122. * @see how to use it on http://piwik.org/faq/how-to/#faq_30
  123. * @throws Exception
  124. * @return void
  125. */
  126. function logme()
  127. {
  128. $password = Common::getRequestVar('password', null, 'string');
  129. $login = Common::getRequestVar('login', null, 'string');
  130. if (Piwik::hasTheUserSuperUserAccess($login)) {
  131. throw new Exception(Piwik::translate('Login_ExceptionInvalidSuperUserAccessAuthenticationMethod', array("logme")));
  132. }
  133. $currentUrl = 'index.php';
  134. if (($idSite = Common::getRequestVar('idSite', false, 'int')) !== false) {
  135. $currentUrl .= '?idSite=' . $idSite;
  136. }
  137. $urlToRedirect = Common::getRequestVar('url', $currentUrl, 'string');
  138. $urlToRedirect = Common::unsanitizeInputValue($urlToRedirect);
  139. $this->authenticateAndRedirect($login, $password, false, $urlToRedirect, $passwordHashed = true);
  140. }
  141. /**
  142. * Authenticate user and password. Redirect if successful.
  143. *
  144. * @param string $login user name
  145. * @param string $password md5 password
  146. * @param bool $rememberMe Remember me?
  147. * @param string $urlToRedirect URL to redirect to, if successfully authenticated
  148. * @return string failure message if unable to authenticate
  149. */
  150. protected function authenticateAndRedirect($login, $password, $rememberMe, $urlToRedirect = false, $passwordHashed = false)
  151. {
  152. Nonce::discardNonce('Login.login');
  153. $this->auth->setLogin($login);
  154. if ($passwordHashed === false) {
  155. $this->auth->setPassword($password);
  156. } else {
  157. $this->auth->setPasswordHash($password);
  158. }
  159. $this->sessionInitializer->initSession($this->auth, $rememberMe);
  160. // remove password reset entry if it exists
  161. $this->passwordResetter->removePasswordResetInfo($login);
  162. if (empty($urlToRedirect)) {
  163. $urlToRedirect = Url::getCurrentUrlWithoutQueryString();
  164. }
  165. Url::redirectToUrl($urlToRedirect);
  166. }
  167. protected function getMessageExceptionNoAccess()
  168. {
  169. $message = Piwik::translate('Login_InvalidNonceOrHeadersOrReferrer', array('<a href="?module=Proxy&action=redirect&url=' . urlencode('http://piwik.org/faq/how-to-install/#faq_98') . '" target="_blank">', '</a>'));
  170. // Should mention trusted_hosts or link to FAQ
  171. return $message;
  172. }
  173. /**
  174. * Reset password action. Stores new password as hash and sends email
  175. * to confirm use.
  176. *
  177. * @param none
  178. */
  179. function resetPassword()
  180. {
  181. $infoMessage = null;
  182. $formErrors = null;
  183. $form = new FormResetPassword();
  184. if ($form->validate()) {
  185. $nonce = $form->getSubmitValue('form_nonce');
  186. if (Nonce::verifyNonce('Login.login', $nonce)) {
  187. $formErrors = $this->resetPasswordFirstStep($form);
  188. if (empty($formErrors)) {
  189. $infoMessage = Piwik::translate('Login_ConfirmationLinkSent');
  190. }
  191. } else {
  192. $formErrors = array($this->getMessageExceptionNoAccess());
  193. }
  194. } else {
  195. // if invalid, display error
  196. $formData = $form->getFormData();
  197. $formErrors = $formData['errors'];
  198. }
  199. $view = new View('@Login/resetPassword');
  200. $view->infoMessage = $infoMessage;
  201. $view->formErrors = $formErrors;
  202. return $view->render();
  203. }
  204. /**
  205. * Saves password reset info and sends confirmation email.
  206. *
  207. * @param QuickForm2 $form
  208. * @return array Error message(s) if an error occurs.
  209. */
  210. private function resetPasswordFirstStep($form)
  211. {
  212. $loginMail = $form->getSubmitValue('form_login');
  213. $password = $form->getSubmitValue('form_password');
  214. try {
  215. $this->passwordResetter->initiatePasswordResetProcess($loginMail, $password);
  216. } catch (Exception $ex) {
  217. Log::debug($ex);
  218. return array($ex->getMessage());
  219. }
  220. return null;
  221. }
  222. /**
  223. * Password reset confirmation action. Finishes the password reset process.
  224. * Users visit this action from a link supplied in an email.
  225. */
  226. public function confirmResetPassword()
  227. {
  228. $errorMessage = null;
  229. $login = Common::getRequestVar('login', '');
  230. $resetToken = Common::getRequestVar('resetToken', '');
  231. try {
  232. $this->passwordResetter->confirmNewPassword($login, $resetToken);
  233. } catch (Exception $ex) {
  234. Log::debug($ex);
  235. $errorMessage = $ex->getMessage();
  236. }
  237. if (is_null($errorMessage)) { // if success, show login w/ success message
  238. // have to do this as super user since redirectToIndex checks if there's a default website ID for
  239. // the current user and if not, doesn't redirect to the requested action. TODO: this behavior is wrong. somehow.
  240. $self = $this;
  241. Access::doAsSuperUser(function () use ($self) {
  242. $self->redirectToIndex(Piwik::getLoginPluginName(), 'resetPasswordSuccess');
  243. });
  244. return null;
  245. } else {
  246. // show login page w/ error. this will keep the token in the URL
  247. return $this->login($errorMessage);
  248. }
  249. }
  250. /**
  251. * The action used after a password is successfully reset. Displays the login
  252. * screen with an extra message. A separate action is used instead of returning
  253. * the HTML in confirmResetPassword so the resetToken won't be in the URL.
  254. */
  255. public function resetPasswordSuccess()
  256. {
  257. return $this->login($errorMessage = null, $infoMessage = Piwik::translate('Login_PasswordChanged'));
  258. }
  259. /**
  260. * Clear session information
  261. *
  262. * @param none
  263. * @return void
  264. */
  265. public static function clearSession()
  266. {
  267. $authCookieName = Config::getInstance()->General['login_cookie_name'];
  268. $cookie = new Cookie($authCookieName);
  269. $cookie->delete();
  270. Session::expireSessionCookie();
  271. }
  272. /**
  273. * Logout current user
  274. *
  275. * @param none
  276. * @return void
  277. */
  278. public function logout()
  279. {
  280. self::clearSession();
  281. $logoutUrl = @Config::getInstance()->General['login_logout_url'];
  282. if (empty($logoutUrl)) {
  283. Piwik::redirectToModule('CoreHome');
  284. } else {
  285. Url::redirectToUrl($logoutUrl);
  286. }
  287. }
  288. }