PageRenderTime 78ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/symphony/content/content.login.php

https://github.com/nils-werner/symphony-2
PHP | 275 lines | 167 code | 48 blank | 60 comment | 37 complexity | 7b5b50922d718b63595be381bb9a24c7 MD5 | raw file
  1. <?php
  2. /**
  3. * @package content
  4. */
  5. /**
  6. * The default Symphony login page that is shown to users who attempt
  7. * to access `SYMPHONY_URL` but are not logged in. This page has logic
  8. * to allow users to reset their passwords should they forget.
  9. */
  10. Class contentLogin extends HTMLPage{
  11. public $failedLoginAttempt = false;
  12. public function __construct(){
  13. parent::__construct();
  14. $this->addHeaderToPage('Content-Type', 'text/html; charset=UTF-8');
  15. $this->Html->setElementStyle('html');
  16. $this->Html->setDTD('<!DOCTYPE html>');
  17. $this->Html->setAttribute('lang', Lang::get());
  18. $this->addElementToHead(new XMLElement('meta', NULL, array('charset' => 'UTF-8')), 0);
  19. $this->addElementToHead(new XMLElement('meta', NULL, array('http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1')), 1);
  20. $this->addElementToHead(new XMLElement('meta', NULL, array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1')), 2);
  21. $this->addStylesheetToHead(APPLICATION_URL . '/assets/css/symphony.css', 'screen', 30);
  22. $this->addStylesheetToHead(APPLICATION_URL . '/assets/css/symphony.forms.css', 'screen', 31);
  23. $this->addStylesheetToHead(APPLICATION_URL . '/assets/css/symphony.frames.css', 'screen', 32);
  24. $this->setTitle(__('%1$s &ndash; %2$s', array(__('Login'), __('Symphony'))));
  25. $this->Body->setAttribute('id', 'login');
  26. Symphony::Profiler()->sample('Page template created', PROFILE_LAP);
  27. }
  28. public function build($context=NULL) {
  29. if($context) $this->_context = $context;
  30. if(isset($_REQUEST['action'])) $this->action();
  31. $this->view();
  32. }
  33. public function view() {
  34. if(isset($this->_context[0]) && in_array(strlen($this->_context[0]), array(6, 8))){
  35. if(!$this->__loginFromToken($this->_context[0])) {
  36. if(Administration::instance()->isLoggedIn()) redirect(SYMPHONY_URL);
  37. }
  38. }
  39. $this->Form = Widget::Form(SYMPHONY_URL . '/login/', 'post');
  40. $this->Form->setAttribute('class', 'frame');
  41. $this->Form->appendChild(new XMLElement('h1', __('Symphony')));
  42. $fieldset = new XMLElement('fieldset');
  43. // Display retrieve password UI
  44. if(isset($this->_context[0]) && $this->_context[0] == 'retrieve-password'):
  45. $this->Form->setAttribute('action', SYMPHONY_URL.'/login/retrieve-password/');
  46. if(isset($this->_email_sent) && $this->_email_sent) {
  47. $fieldset->appendChild(new XMLElement('p', __('An email containing a customised login link has been sent to %s. It will expire in 2 hours.', array(
  48. '<code>' . $this->_email_sent_to . '</code>')
  49. )));
  50. $fieldset->appendChild(new XMLElement('p', Widget::Anchor(__('Login'), SYMPHONY_URL.'/login/', null)));
  51. $this->Form->appendChild($fieldset);
  52. }
  53. else {
  54. $fieldset->appendChild(new XMLElement('p', __('Enter your email address or username to be sent further instructions for logging in.')));
  55. $label = Widget::Label(__('Email Address or Username'));
  56. $label->appendChild(Widget::Input('email', General::sanitize($_POST['email']), 'text', array('autofocus' => 'autofocus')));
  57. if(isset($this->_email_sent) && !$this->_email_sent) {
  58. $label = Widget::Error($label, __('Unfortunately no account was found using this information.'));
  59. }
  60. $fieldset->appendChild($label);
  61. $this->Form->appendChild($fieldset);
  62. $div = new XMLElement('div', NULL, array('class' => 'actions'));
  63. $div->appendChild(
  64. new XMLElement('button', __('Send Email'), array('name' => 'action[reset]', 'type' => 'submit'))
  65. );
  66. $div->appendChild(
  67. Widget::Anchor(__('Cancel'), SYMPHONY_URL.'/login/', null, 'action-link')
  68. );
  69. $this->Form->appendChild($div);
  70. }
  71. // Normal login
  72. else:
  73. $fieldset->appendChild(new XMLElement('legend', __('Login')));
  74. // Display error message
  75. if($this->failedLoginAttempt){
  76. $p = new XMLElement('p');
  77. $p = Widget::Error($p, __('The login details provided are incorrect.'));
  78. $fieldset->appendChild($p);
  79. }
  80. // Username
  81. $label = Widget::Label(__('Username'));
  82. $username = Widget::Input('username', isset($_POST['username']) ? General::sanitize($_POST['username']) : null);
  83. if(!$this->failedLoginAttempt) {
  84. $username->setAttribute('autofocus', 'autofocus');
  85. }
  86. $label->appendChild($username);
  87. if(isset($_POST['action'], $_POST['action']['login']) && empty($_POST['username'])) {
  88. $username->setAttribute('autofocus', 'autofocus');
  89. $label = Widget::Error($label, __('No username was entered.'));
  90. }
  91. $fieldset->appendChild($label);
  92. // Password
  93. $label = Widget::Label(__('Password'));
  94. $password = Widget::Input('password', NULL, 'password');
  95. $label->appendChild($password);
  96. if(isset($_POST['action'], $_POST['action']['login']) && empty($_POST['password'])) {
  97. $password->setAttribute('autofocus', 'autofocus');
  98. $label = Widget::Error($label, __('No password was entered.'));
  99. }
  100. else if($this->failedLoginAttempt) {
  101. $password->setAttribute('autofocus', 'autofocus');
  102. }
  103. $fieldset->appendChild($label);
  104. $this->Form->appendChild($fieldset);
  105. // Actions
  106. $div = new XMLElement('div', NULL, array('class' => 'actions'));
  107. $div->appendChild(
  108. new XMLElement('button', __('Login'), array('name' => 'action[login]', 'type' => 'submit', 'accesskey' => 's'))
  109. );
  110. $div->appendChild(
  111. Widget::Anchor(__('Retrieve password?'), SYMPHONY_URL.'/login/retrieve-password/', null, 'action-link')
  112. );
  113. $this->Form->appendChild($div);
  114. if(isset($this->_context['redirect'])) {
  115. $this->Form->appendChild(
  116. Widget::Input('redirect', SYMPHONY_URL . General::sanitize($this->_context['redirect']), 'hidden')
  117. );
  118. }
  119. endif;
  120. $this->Body->appendChild($this->Form);
  121. }
  122. public function action() {
  123. if(isset($_POST['action'])) {
  124. $actionParts = array_keys($_POST['action']);
  125. $action = end($actionParts);
  126. // Login Attempted
  127. if($action == 'login'):
  128. if(empty($_POST['username']) || empty($_POST['password']) || !Administration::instance()->login($_POST['username'], $_POST['password'])) {
  129. /**
  130. * A failed login attempt into the Symphony backend
  131. *
  132. * @delegate AuthorLoginFailure
  133. * @since Symphony 2.2
  134. * @param string $context
  135. * '/login/'
  136. * @param string $username
  137. * The username of the Author who attempted to login.
  138. */
  139. Symphony::ExtensionManager()->notifyMembers('AuthorLoginFailure', '/login/', array('username' => Symphony::Database()->cleanValue($_POST['username'])));
  140. $this->failedLoginAttempt = true;
  141. }
  142. else {
  143. /**
  144. * A successful login attempt into the Symphony backend
  145. *
  146. * @delegate AuthorLoginSuccess
  147. * @since Symphony 2.2
  148. * @param string $context
  149. * '/login/'
  150. * @param string $username
  151. * The username of the Author who logged in.
  152. */
  153. Symphony::ExtensionManager()->notifyMembers('AuthorLoginSuccess', '/login/', array('username' => Symphony::Database()->cleanValue($_POST['username'])));
  154. isset($_POST['redirect']) ? redirect($_POST['redirect']) : redirect(SYMPHONY_URL);
  155. }
  156. // Reset of password requested
  157. elseif($action == 'reset'):
  158. $author = Symphony::Database()->fetchRow(0, sprintf("
  159. SELECT `id`, `email`, `first_name`
  160. FROM `tbl_authors`
  161. WHERE `email` = '%1\$s' OR `username` = '%1\$s'
  162. ", Symphony::Database()->cleanValue($_POST['email'])
  163. ));
  164. if(!empty($author)){
  165. Symphony::Database()->delete('tbl_forgotpass', " `expiry` < '".DateTimeObj::getGMT('c')."' ");
  166. if(!$token = Symphony::Database()->fetchVar('token', 0, "SELECT `token` FROM `tbl_forgotpass` WHERE `expiry` > '".DateTimeObj::getGMT('c')."' AND `author_id` = ".$author['id'])){
  167. $token = substr(SHA1::hash(time() . rand(0, 1000)), 0, 6);
  168. Symphony::Database()->insert(array(
  169. 'author_id' => $author['id'],
  170. 'token' => $token,
  171. 'expiry' => DateTimeObj::getGMT('c', time() + (120 * 60))
  172. ), 'tbl_forgotpass');
  173. }
  174. try{
  175. $email = Email::create();
  176. $email->recipients = $author['email'];
  177. $email->subject = __('New Symphony Account Password');
  178. $email->text_plain = __('Hi %s,', array($author['first_name'])) . PHP_EOL .
  179. __('A new password has been requested for your account. Login using the following link, and change your password via the Authors area:') . PHP_EOL .
  180. PHP_EOL . ' ' . SYMPHONY_URL . "/login/{$token}/" . PHP_EOL . PHP_EOL .
  181. __('It will expire in 2 hours. If you did not ask for a new password, please disregard this email.') . PHP_EOL . PHP_EOL .
  182. __('Best Regards,') . PHP_EOL .
  183. __('The Symphony Team');
  184. $email->send();
  185. $this->_email_sent = true;
  186. $this->_email_sent_to = $author['email']; // Set this so we can display a customised message
  187. }
  188. catch(Exception $e) {}
  189. /**
  190. * When a password reset has occurred and after the Password
  191. * Reset email has been sent.
  192. *
  193. * @delegate AuthorPostPasswordResetSuccess
  194. * @since Symphony 2.2
  195. * @param string $context
  196. * '/login/'
  197. * @param integer $author_id
  198. * The ID of the Author who requested the password reset
  199. */
  200. Symphony::ExtensionManager()->notifyMembers('AuthorPostPasswordResetSuccess', '/login/', array('author_id' => $author['id']));
  201. }
  202. else {
  203. /**
  204. * When a password reset has been attempted, but Symphony doesn't
  205. * recognise the credentials the user has given.
  206. *
  207. * @delegate AuthorPostPasswordResetFailure
  208. * @since Symphony 2.2
  209. * @param string $context
  210. * '/login/'
  211. * @param string $email
  212. * The sanitised Email of the Author who tried to request the password reset
  213. */
  214. Symphony::ExtensionManager()->notifyMembers('AuthorPostPasswordResetFailure', '/login/', array('email' => Symphony::Database()->cleanValue($_POST['email'])));
  215. $this->_email_sent = false;
  216. }
  217. endif;
  218. }
  219. }
  220. public function __loginFromToken($token){
  221. // If token is invalid, return to login page
  222. if(!Administration::instance()->loginFromToken($token)) return false;
  223. // If token is valid and is an 8 char shortcut
  224. if(strlen($token) != 6) redirect(SYMPHONY_URL); // Regular token-based login
  225. return false;
  226. }
  227. }