From 8034eb10013c77a8b466ed3e2042f9ef64da33e1 Mon Sep 17 00:00:00 2001 From: Seth Date: Fri, 24 Sep 2021 12:37:56 -0400 Subject: [PATCH] Check redirect url; only redirect to urls with local host --- app/controllers/system/AuthController.php | 337 +++++++++++----------- 1 file changed, 175 insertions(+), 162 deletions(-) diff --git a/app/controllers/system/AuthController.php b/app/controllers/system/AuthController.php index 09a66f25f9..10486ae2f5 100644 --- a/app/controllers/system/AuthController.php +++ b/app/controllers/system/AuthController.php @@ -7,7 +7,7 @@ * ---------------------------------------------------------------------- * * Software by Whirl-i-Gig (http://www.whirl-i-gig.com) - * Copyright 2007-2018 Whirl-i-Gig + * Copyright 2007-2021 Whirl-i-Gig * * For more information visit http://www.CollectiveAccess.org * @@ -25,190 +25,203 @@ * * ---------------------------------------------------------------------- */ - require_once(__CA_LIB_DIR__.'/WidgetManager.php'); - require_once(__CA_LIB_DIR__.'/Auth/AuthenticationManager.php'); - - class AuthController extends ActionController { - # ------------------------------------------------------- +require_once(__CA_LIB_DIR__.'/WidgetManager.php'); +require_once(__CA_LIB_DIR__.'/Auth/AuthenticationManager.php'); + +class AuthController extends ActionController { + # ------------------------------------------------------- + + # ------------------------------------------------------- + public function __construct(&$po_request, &$po_response, $pa_view_paths=null) { + parent::__construct($po_request, $po_response, $pa_view_paths); + if (AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_USE_ADAPTER_LOGIN_FORM__) && !in_array($this->request->getAction(), ['logout', 'callback'])) { + $po_response->setRedirect($po_request->getBaseUrlPath().'/'.$po_request->getScriptName().'/'.$po_request->config->get("auth_login_welcome_path")); + $po_response->sendResponse(); + exit; + } + } + # ------------------------------------------------------- + public function Login() { + global $g_ui_locale; + if (isset($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) { + if(!initializeLocale($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) die("Error loading locale ".$g_ui_locale); + } + // Redirect to the default action + $vs_redirect = $this->_getRedirectUrl(); + + $this->getView()->setVar('redirect', $vs_redirect); + $this->render('login_html.php'); + } + # ------------------------------------------------------- + public function DoLogin() { + if (!caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { + $this->view->setVar('notifications', $this->notification->getNotifications()); + $this->render('login_html.php'); + return; + } + global $g_ui_locale; - # ------------------------------------------------------- - public function __construct(&$po_request, &$po_response, $pa_view_paths=null) { - parent::__construct($po_request, $po_response, $pa_view_paths); - if (AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_USE_ADAPTER_LOGIN_FORM__) && !in_array($this->request->getAction(), ['logout', 'callback'])) { - $po_response->setRedirect($po_request->getBaseUrlPath().'/'.$po_request->getScriptName().'/'.$po_request->config->get("auth_login_welcome_path")); - $po_response->sendResponse(); - exit; - } - } - # ------------------------------------------------------- - - public function Login() { - global $g_ui_locale; + $vs_redirect_url = $this->_getRedirectUrl(); + if (!$this->request->doAuthentication(array('dont_redirect_to_login' => true, 'redirect' => $vs_redirect_url, 'noPublicUsers' => true, 'user_name' => $this->request->getParameter('username', pString), 'password' => $this->request->getParameter('password', pString)))) { + $this->notification->addNotification(_t("Login was invalid"), __NOTIFICATION_TYPE_ERROR__); + + $this->view->setVar('notifications', $this->notification->getNotifications()); if (isset($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) { if(!initializeLocale($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) die("Error loading locale ".$g_ui_locale); } - // Redirect to the default action - $vs_redirect = $this->request->getParameter('redirect', pString); - $this->getView()->setVar('redirect', $vs_redirect); - $this->render('login_html.php'); - } - # ------------------------------------------------------- - public function DoLogin() { - if (!caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { - $this->view->setVar('notifications', $this->notification->getNotifications()); - $this->render('login_html.php'); - return; - } - global $g_ui_locale; - - $vs_redirect_url = $this->request->getParameter('redirect', pString) ?: caNavUrl($this->request, null, null, null); - if (!$this->request->doAuthentication(array('dont_redirect_to_login' => true, 'redirect' => $vs_redirect_url, 'noPublicUsers' => true, 'user_name' => $this->request->getParameter('username', pString), 'password' => $this->request->getParameter('password', pString)))) { - $this->notification->addNotification(_t("Login was invalid"), __NOTIFICATION_TYPE_ERROR__); - - $this->view->setVar('notifications', $this->notification->getNotifications()); - if (isset($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) { - if(!initializeLocale($_COOKIE['CA_'.__CA_APP_NAME__.'_ui_locale'])) die("Error loading locale ".$g_ui_locale); - } - $this->render('login_html.php'); - } else { - // - // Reset locale globals - // - global $g_ui_locale_id, $g_ui_locale, $g_ui_units_pref, $_, $_locale; - $g_ui_locale_id = $this->request->user->getPreferredUILocaleID(); // get current UI locale as locale_id (available as global) - $g_ui_locale = $this->request->user->getPreferredUILocale(); // get current UI locale as locale string (available as global) - $g_ui_units_pref = $this->request->user->getPreference('units'); // user's selected display units for measurements (available as global) - - if(!initializeLocale($g_ui_locale)) die("Error loading locale ".$g_ui_locale); - MemoryCache::flush('translation'); - AppNavigation::clearMenuBarCache($this->request); // want to clear menu bar on login - - // Notify the user of the good news - $this->notification->addNotification(_t("You are now logged in"), __NOTIFICATION_TYPE_INFO__); - - // Jump to redirect if set - if ($vs_redirect_url) $this->redirect($vs_redirect_url); - $this->render('welcome_html.php'); - } - } - # ------------------------------------------------------- - public function Welcome() { - AppNavigation::clearMenuBarCache($this->request); // clear menu bar cache on welcome (stealth debugging tool) - - $this->render('welcome_html.php'); - } - # ------------------------------------------------------- - public function Logout() { - $this->request->deauthenticate(); - - AppNavigation::clearMenuBarCache($this->request); // clear menu bar cache on logout just in case - $this->notification->addNotification(_t("You are now logged out"), __NOTIFICATION_TYPE_INFO__); - $this->view->setVar('notifications', $this->notification->getNotifications()); - $this->render('logged_out_html.php'); - } - # ------------------------------------------------------- - public function Forgot() { - if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } - - $this->render('forgot_password_html.php'); - } - # ------------------------------------------------------- - public function RequestPassword() { - if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } - if (caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { - $vs_username = $this->getRequest()->getParameter('username',pString); - $t_user = new ca_users(); - - if($t_user->load($vs_username)) { - $t_user->requestPasswordReset(); - } else { - sleep(2); - } - } - // render the same static view no matter if something was actually done. - // otherwise you could figure out which user names exist and which don't - $this->render('password_reset_instructions_html.php'); + $this->render('login_html.php'); + } else { + // + // Reset locale globals + // + global $g_ui_locale_id, $g_ui_locale, $g_ui_units_pref, $_, $_locale; + $g_ui_locale_id = $this->request->user->getPreferredUILocaleID(); // get current UI locale as locale_id (available as global) + $g_ui_locale = $this->request->user->getPreferredUILocale(); // get current UI locale as locale string (available as global) + $g_ui_units_pref = $this->request->user->getPreference('units'); // user's selected display units for measurements (available as global) + + if(!initializeLocale($g_ui_locale)) die("Error loading locale ".$g_ui_locale); + MemoryCache::flush('translation'); + AppNavigation::clearMenuBarCache($this->request); // want to clear menu bar on login + + // Notify the user of the good news + $this->notification->addNotification(_t("You are now logged in"), __NOTIFICATION_TYPE_INFO__); + + // Jump to redirect if set + if ($vs_redirect_url) $this->redirect($vs_redirect_url); + $this->render('welcome_html.php'); } - # ------------------------------------------------------- - public function InitReset() { - if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } + } + # ------------------------------------------------------- + public function Welcome() { + AppNavigation::clearMenuBarCache($this->request); // clear menu bar cache on welcome (stealth debugging tool) + + $this->render('welcome_html.php'); + } + # ------------------------------------------------------- + public function Logout() { + $this->request->deauthenticate(); + + AppNavigation::clearMenuBarCache($this->request); // clear menu bar cache on logout just in case + $this->notification->addNotification(_t("You are now logged out"), __NOTIFICATION_TYPE_INFO__); + $this->view->setVar('notifications', $this->notification->getNotifications()); + $this->render('logged_out_html.php'); + } + # ------------------------------------------------------- + public function Forgot() { + if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } - $vs_token = $this->getRequest()->getParameter('token',pString); + $this->render('forgot_password_html.php'); + } + # ------------------------------------------------------- + public function RequestPassword() { + if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } + if (caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { $vs_username = $this->getRequest()->getParameter('username',pString); $t_user = new ca_users(); - $vb_render_form = false; if($t_user->load($vs_username)) { - if($t_user->isValidToken($vs_token)) { - $vb_render_form = true; - } + $t_user->requestPasswordReset(); + } else { + sleep(2); } + } + // render the same static view no matter if something was actually done. + // otherwise you could figure out which user names exist and which don't + $this->render('password_reset_instructions_html.php'); + } + # ------------------------------------------------------- + public function InitReset() { + if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } - $this->view->setVar('renderForm', $vb_render_form); - $this->view->setVar('token', $vs_token); - $this->view->setVar('username', $vs_username); + $vs_token = $this->getRequest()->getParameter('token',pString); + $vs_username = $this->getRequest()->getParameter('username',pString); + $t_user = new ca_users(); - $this->render('password_reset_form_html.php'); + $vb_render_form = false; + if($t_user->load($vs_username)) { + if($t_user->isValidToken($vs_token)) { + $vb_render_form = true; + } } - # ------------------------------------------------------- - public function DoReset() { - if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } - if (!caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { - $this->Login(); - return; - } - - $vs_token = $this->getRequest()->getParameter('token',pString); - $vs_username = $this->getRequest()->getParameter('username',pString); - $t_user = new ca_users(); - $vs_pw = $this->getRequest()->getParameter('password',pString); - $vs_pw_check = $this->getRequest()->getParameter('password2',pString); + $this->view->setVar('renderForm', $vb_render_form); + $this->view->setVar('token', $vs_token); + $this->view->setVar('username', $vs_username); - if($t_user->load($vs_username)) { - if($t_user->isValidToken($vs_token)) { - // no password match - if($vs_pw !== $vs_pw_check) { + $this->render('password_reset_form_html.php'); + } + # ------------------------------------------------------- + public function DoReset() { + if(!AuthenticationManager::supports(__CA_AUTH_ADAPTER_FEATURE_RESET_PASSWORDS__)) { $this->Login(); return; } + if (!caValidateCSRFToken($this->request, null, ['notifications' => $this->notification])) { + $this->Login(); + return; + } - $this->notification->addNotification(_t("Passwords did not match. Please try again."), __NOTIFICATION_TYPE_ERROR__); - $this->view->setVar('notifications', $this->notification->getNotifications()); + $vs_token = $this->getRequest()->getParameter('token',pString); + $vs_username = $this->getRequest()->getParameter('username',pString); + $t_user = new ca_users(); - $this->view->setVar('renderForm', true); - $this->view->setVar('token', $vs_token); - $this->view->setVar('username', $vs_username); + $vs_pw = $this->getRequest()->getParameter('password',pString); + $vs_pw_check = $this->getRequest()->getParameter('password2',pString); - $this->render('password_reset_form_html.php'); - } else { - $t_user->set('password', $vs_pw); - $t_user->setMode(ACCESS_WRITE); - $t_user->update(); + if($t_user->load($vs_username)) { + if($t_user->isValidToken($vs_token)) { + // no password match + if($vs_pw !== $vs_pw_check) { - $this->notification->addNotification(_t("Password was successfully changed. You can now log in with your new password."), __NOTIFICATION_TYPE_INFO__); - $this->view->setVar('notifications', $this->notification->getNotifications()); + $this->notification->addNotification(_t("Passwords did not match. Please try again."), __NOTIFICATION_TYPE_ERROR__); + $this->view->setVar('notifications', $this->notification->getNotifications()); - $this->Login(); - } + $this->view->setVar('renderForm', true); + $this->view->setVar('token', $vs_token); + $this->view->setVar('username', $vs_username); + + $this->render('password_reset_form_html.php'); + } else { + $t_user->set('password', $vs_pw); + $t_user->setMode(ACCESS_WRITE); + $t_user->update(); + + $this->notification->addNotification(_t("Password was successfully changed. You can now log in with your new password."), __NOTIFICATION_TYPE_INFO__); + $this->view->setVar('notifications', $this->notification->getNotifications()); + + $this->Login(); } } + } + } + # ------------------------------------------------------- + # Authentication callback handler + # Passes control to authentication manager callback handler + # Authentication adapters for schemes like Okta can use this to + # implement required control flow elements + # ------------------------------------------------------- + /** + * + */ + public function callback() { + try { + AuthenticationManager::callback(); + } catch (Exception $e) { + $this->notification->addNotification($e->getMessage(), __NOTIFICATION_TYPE_ERROR__); + $this->view->setVar('notifications', $this->notification->getNotifications()); + $this->render('auth_error_html.php'); } - # ------------------------------------------------------- - # Authentication callback handler - # Passes control to authentication manager callback handler - # Authentication adapters for schemes like Okta can use this to - # implement required control flow elements - # ------------------------------------------------------- - /** - * - */ - public function callback() { - try { - AuthenticationManager::callback(); - } catch (Exception $e) { - $this->notification->addNotification($e->getMessage(), __NOTIFICATION_TYPE_ERROR__); - $this->view->setVar('notifications', $this->notification->getNotifications()); - $this->render('auth_error_html.php'); - } + } + # ------------------------------------------------------- + /** + * + */ + private function _getRedirectUrl() { + $host = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST']; + $redirect_url = $this->request->getParameter('redirect', pString, null, ['forcePurify' => true]) ?: caNavUrl($this->request, null, null, null); + + if(!preg_match('!^'.preg_quote($host, '!').'!', $redirect_url)) { + $redirect_url = null; } - # ------------------------------------------------------- - } + return $redirect_url; + } + # ------------------------------------------------------- +}