PageRenderTime 58ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/concrete/controllers/single_page/login.php

http://github.com/concrete5/concrete5
PHP | 435 lines | 314 code | 55 blank | 66 comment | 49 complexity | 2298505737768d5e1deb0912702cb65b MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. <?php
  2. namespace Concrete\Controller\SinglePage;
  3. use Concrete\Core\Authentication\AuthenticationType;
  4. use Concrete\Core\Authentication\AuthenticationTypeFailureException;
  5. use Concrete\Core\Http\ResponseFactoryInterface;
  6. use Concrete\Core\Localization\Localization;
  7. use Concrete\Core\Logging\Channels;
  8. use Concrete\Core\Logging\LoggerAwareInterface;
  9. use Concrete\Core\Logging\LoggerAwareTrait;
  10. use Concrete\Core\Routing\RedirectResponse;
  11. use Concrete\Core\User\PostLoginLocation;
  12. use Exception;
  13. use PageController;
  14. use Concrete\Core\User\User;
  15. use UserAttributeKey;
  16. use UserInfo;
  17. class Login extends PageController implements LoggerAwareInterface
  18. {
  19. use LoggerAwareTrait;
  20. public function getLoggerChannel()
  21. {
  22. return Channels::CHANNEL_SECURITY;
  23. }
  24. public $helpers = ['form'];
  25. protected $locales = [];
  26. public function on_before_render()
  27. {
  28. if ($this->error->has()) {
  29. $this->set('error', $this->error);
  30. }
  31. }
  32. /* automagically run by the controller once we're done with the current method */
  33. /* method is passed to this method, the method that we were just finished running */
  34. public function account_deactivated()
  35. {
  36. $config = $this->app->make('config');
  37. $this->error->add(t($config->get('concrete.user.deactivation.message')));
  38. }
  39. public function session_invalidated()
  40. {
  41. $this->error->add(t('Your session has expired. Please sign in again.'));
  42. }
  43. /**
  44. * Concrete5_Controller_Login::callback
  45. * Call an AuthenticationTypeController method throw a uri.
  46. * Use: /login/TYPE/METHOD/PARAM1/.../PARAM10.
  47. *
  48. * @param string $type
  49. * @param string $method
  50. * @param null $a
  51. * @param null $b
  52. * @param null $c
  53. * @param null $d
  54. * @param null $e
  55. * @param null $f
  56. * @param null $g
  57. * @param null $h
  58. * @param null $i
  59. * @param null $j
  60. *
  61. * @throws \Concrete\Core\Authentication\AuthenticationTypeFailureException
  62. * @throws \Exception
  63. */
  64. public function callback($type = null, $method = 'callback', $a = null, $b = null, $c = null, $d = null, $e = null, $f = null, $g = null, $h = null, $i = null, $j = null)
  65. {
  66. if (!$type) {
  67. return $this->view();
  68. }
  69. $at = AuthenticationType::getByHandle($type);
  70. if (!$at || !$at->isEnabled()) {
  71. throw new AuthenticationTypeFailureException(t('Invalid authentication type.'));
  72. }
  73. $this->set('authType', $at);
  74. if (!method_exists($at->controller, $method)) {
  75. return $this->view();
  76. }
  77. if ($method != 'callback') {
  78. if (!is_array($at->controller->apiMethods) || !in_array($method, $at->controller->apiMethods)) {
  79. return $this->view();
  80. }
  81. }
  82. try {
  83. $params = func_get_args();
  84. array_shift($params);
  85. array_shift($params);
  86. $this->view();
  87. $this->set('authTypeParams', $params);
  88. $this->set('authTypeElement', $method);
  89. } catch (Exception $e) {
  90. if ($e instanceof AuthenticationTypeFailureException) {
  91. // Throw again if this is a big`n
  92. throw $e;
  93. }
  94. $this->error->add($e->getMessage());
  95. }
  96. }
  97. /**
  98. * Concrete5_Controller_Login::authenticate
  99. * Authenticate the user using a specific authentication type.
  100. *
  101. * @param $type AuthenticationType handle
  102. */
  103. public function authenticate($type = '')
  104. {
  105. $valt = $this->app->make('token');
  106. if (!$valt->validate('login_' . $type)) {
  107. $this->error->add($valt->getErrorMessage());
  108. } else {
  109. try {
  110. $at = AuthenticationType::getByHandle($type);
  111. if (!$at->isEnabled()) {
  112. throw new AuthenticationTypeFailureException(t('Invalid authentication type.'));
  113. }
  114. $user = $at->controller->authenticate();
  115. if ($user && $user->isRegistered()) {
  116. return $this->finishAuthentication($at, $user);
  117. }
  118. } catch (Exception $e) {
  119. $this->error->add($e->getMessage());
  120. }
  121. }
  122. if (isset($at)) {
  123. $this->set('lastAuthType', $at);
  124. }
  125. $this->view();
  126. }
  127. /**
  128. * @param AuthenticationType $type Required
  129. *
  130. * @throws Exception
  131. */
  132. public function finishAuthentication(
  133. AuthenticationType $type,
  134. User $u
  135. )
  136. {
  137. $this->app->instance(User::class, $u);
  138. if (!$type || !($type instanceof AuthenticationType)) {
  139. return $this->view();
  140. }
  141. $config = $this->app->make('config');
  142. if ($config->get('concrete.i18n.choose_language_login')) {
  143. $userLocale = $this->post('USER_LOCALE');
  144. if (is_string($userLocale) && ($userLocale !== '')) {
  145. if ($userLocale !== Localization::BASE_LOCALE) {
  146. $availableLocales = Localization::getAvailableInterfaceLanguages();
  147. if (!in_array($userLocale, $availableLocales)) {
  148. $userLocale = '';
  149. }
  150. }
  151. if ($userLocale !== '') {
  152. if (Localization::activeLocale() !== $userLocale) {
  153. Localization::changeLocale($userLocale);
  154. }
  155. $u->setUserDefaultLanguage($userLocale);
  156. }
  157. }
  158. }
  159. $ui = UserInfo::getByID($u->getUserID());
  160. $aks = UserAttributeKey::getRegistrationList();
  161. $unfilled = array_values(
  162. array_filter(
  163. $aks,
  164. function ($ak) use ($ui) {
  165. return $ak->isAttributeKeyRequiredOnRegister() && !is_object($ui->getAttributeValueObject($ak));
  166. }));
  167. if (count($unfilled)) {
  168. $u->logout(false);
  169. if (!$this->error) {
  170. $this->on_start();
  171. }
  172. $this->set('required_attributes', $unfilled);
  173. $this->set('u', $u);
  174. $session = $this->app->make('session');
  175. $session->set('uRequiredAttributeUser', $u->getUserID());
  176. $session->set('uRequiredAttributeUserAuthenticationType', $type->getAuthenticationTypeHandle());
  177. $this->view();
  178. return $this->getViewObject()->render();
  179. }
  180. $u->setLastAuthType($type);
  181. $ue = new \Concrete\Core\User\Event\User($u);
  182. $this->app->make('director')->dispatch('on_user_login', $ue);
  183. return new RedirectResponse(
  184. $this->app->make('url/manager')->resolve(['/login', 'login_complete'])
  185. );
  186. }
  187. public function login_complete()
  188. {
  189. // Move this functionality to a redirected endpoint rather than from within the previous method because
  190. // session isn't set until we redirect and reload.
  191. $u = $this->app->make(User::class);
  192. if (!$this->error) {
  193. $this->error = $this->app->make('helper/validation/error');
  194. }
  195. if ($u->isRegistered()) {
  196. $pll = $this->app->make(PostLoginLocation::class);
  197. $response = $pll->getPostLoginRedirectResponse(true);
  198. return $response;
  199. } else {
  200. $session = $this->app->make('session');
  201. $this->logger->notice(
  202. t('Session made it to login_complete but was not attached to an authenticated session.'),
  203. ['session' => $session->getId(), 'ip_address' => $_SERVER['REMOTE_ADDR']]
  204. );
  205. $this->error->add(t('User is not registered. Check your authentication controller.'));
  206. $u->logout();
  207. }
  208. }
  209. public function on_start()
  210. {
  211. $config = $this->app->make('config');
  212. $this->error = $this->app->make('helper/validation/error');
  213. $this->set('valt', $this->app->make('helper/validation/token'));
  214. $txt = $this->app->make('helper/text');
  215. if (isset($_GET['uName']) && strlen($_GET['uName'])
  216. ) { // pre-populate the username if supplied, if its an email address with special characters the email needs to be urlencoded first,
  217. $this->set('uName', trim($txt->email($_GET['uName'])));
  218. }
  219. $loc = Localization::getInstance();
  220. $loc->pushActiveContext(Localization::CONTEXT_SITE);
  221. if ($config->get('concrete.user.registration.email_registration')) {
  222. $this->set('uNameLabel', t('Email Address'));
  223. } else {
  224. $this->set('uNameLabel', t('Username'));
  225. }
  226. $languages = [];
  227. $locales = [];
  228. if ($config->get('concrete.i18n.choose_language_login')) {
  229. $languages = Localization::getAvailableInterfaceLanguages();
  230. if (count($languages) > 0) {
  231. array_unshift($languages, Localization::BASE_LOCALE);
  232. }
  233. $locales = [];
  234. foreach ($languages as $lang) {
  235. $locales[$lang] = \Punic\Language::getName($lang, $lang);
  236. }
  237. asort($locales);
  238. $locales = array_merge(['' => tc('Default locale', '** Default')], $locales);
  239. }
  240. $loc->popActiveContext();
  241. $this->locales = $locales;
  242. $this->set('locales', $locales);
  243. }
  244. /**
  245. * @deprecated Use the getPostLoginUrl method of \Concrete\Core\User\PostLoginLocation
  246. *
  247. * @see \Concrete\Core\User\PostLoginLocation::getPostLoginUrl()
  248. *
  249. * @return string
  250. */
  251. public function getRedirectUrl()
  252. {
  253. $pll = $this->app->make(PostLoginLocation::class);
  254. $url = $pll->getPostLoginUrl(true);
  255. return $url;
  256. }
  257. /**
  258. * @deprecated Use the getSessionPostLoginUrl method of \Concrete\Core\User\PostLoginLocation
  259. *
  260. * @see \Concrete\Core\User\PostLoginLocation::getSessionPostLoginUrl()
  261. *
  262. * @return string|false
  263. */
  264. public function getRedirectUrlFromSession()
  265. {
  266. $pll = $this->app->make(PostLoginLocation::class);
  267. $url = $pll->getSessionPostLoginUrl(true);
  268. return $url === '' ? false : $url;
  269. }
  270. public function view($type = null, $element = 'form')
  271. {
  272. $this->requireAsset('javascript', 'backstretch');
  273. $this->set('authTypeParams', $this->getSets());
  274. $user = $this->app->make(User::class);
  275. $this->set('user', $user);
  276. if (strlen($type)) {
  277. try {
  278. $at = AuthenticationType::getByHandle($type);
  279. if ($at->isEnabled()) {
  280. $this->set('authType', $at);
  281. $this->set('authTypeElement', $element);
  282. }
  283. } catch (\Exception $e) {
  284. // Don't fail loudly
  285. }
  286. }
  287. }
  288. public function fill_attributes()
  289. {
  290. try {
  291. $session = $this->app->make('session');
  292. if (!$session->has('uRequiredAttributeUser') ||
  293. intval($session->get('uRequiredAttributeUser')) < 1 ||
  294. !$session->has('uRequiredAttributeUserAuthenticationType') ||
  295. !$session->get('uRequiredAttributeUserAuthenticationType')
  296. ) {
  297. $session->remove('uRequiredAttributeUser');
  298. $session->remove('uRequiredAttributeUserAuthenticationType');
  299. throw new Exception(t('Invalid Request, please attempt login again.'));
  300. }
  301. User::loginByUserID($session->get('uRequiredAttributeUser'));
  302. $session->remove('uRequiredAttributeUser');
  303. $u = $this->app->make(User::class);
  304. $at = AuthenticationType::getByHandle($session->get('uRequiredAttributeUserAuthenticationType'));
  305. $session->remove('uRequiredAttributeUserAuthenticationType');
  306. if (!$at || !$at->isEnabled()) {
  307. throw new Exception(t('Invalid Authentication Type'));
  308. }
  309. $ui = UserInfo::getByID($u->getUserID());
  310. $aks = UserAttributeKey::getRegistrationList();
  311. $unfilled = array_values(
  312. array_filter(
  313. $aks,
  314. function ($ak) use ($ui) {
  315. return $ak->isAttributeKeyRequiredOnRegister() && !is_object($ui->getAttributeValueObject($ak));
  316. }));
  317. $saveAttributes = [];
  318. foreach ($unfilled as $attribute) {
  319. $controller = $attribute->getController();
  320. $validator = $controller->getValidator();
  321. $response = $validator->validateSaveValueRequest($controller, $this->request);
  322. /* @var \Concrete\Core\Validation\ResponseInterface $response */
  323. if ($response->isValid()) {
  324. $saveAttributes[] = $attribute;
  325. } else {
  326. $error = $response->getErrorObject();
  327. $this->error->add($error);
  328. }
  329. }
  330. if (count($saveAttributes) > 0) {
  331. $ui->saveUserAttributesForm($saveAttributes);
  332. }
  333. return $this->finishAuthentication($at, $u);
  334. } catch (Exception $e) {
  335. $this->error->add($e->getMessage());
  336. }
  337. }
  338. /**
  339. * @deprecated
  340. */
  341. public function logout($token = false)
  342. {
  343. if ($this->app->make('token')->validate('logout', $token)) {
  344. $u = $this->app->make(User::class);
  345. $u->logout();
  346. $this->redirect('/');
  347. }
  348. }
  349. /**
  350. * @param $token
  351. *
  352. * @return \Symfony\Component\HttpFoundation\Response
  353. */
  354. public function do_logout($token = false)
  355. {
  356. $factory = $this->app->make(ResponseFactoryInterface::class);
  357. /* @var ResponseFactoryInterface $factory */
  358. $valt = $this->app->make('token');
  359. /* @var \Concrete\Core\Validation\CSRF\Token $valt */
  360. if ($valt->validate('do_logout', $token)) {
  361. // Resolve the current logged in user and log them out
  362. $this->app->make(User::class)->logout();
  363. // Determine the destination URL
  364. $url = $this->app->make('url/manager')->resolve(['/']);
  365. // Return a new redirect to the homepage.
  366. return $factory->redirect((string) $url, 302);
  367. }
  368. return $factory->error($valt->getErrorMessage());
  369. }
  370. public function forward($cID = 0)
  371. {
  372. $nh = $this->app->make('helper/validation/numbers');
  373. if ($nh->integer($cID, 1)) {
  374. $rcID = (int) $cID;
  375. $this->set('rcID', $rcID);
  376. $pll = $this->app->make(PostLoginLocation::class);
  377. $pll->setSessionPostLoginUrl($rcID);
  378. }
  379. }
  380. }