1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-15 03:05:26 +02:00

Update ProcessLogin to have better support for remembering requested URL prior to and after login. It now supports query strings beyond just "?id=123".

This commit is contained in:
Ryan Cramer
2020-10-02 12:17:26 -04:00
parent 33cbec49c1
commit 8dbfd4cca2

View File

@@ -22,7 +22,8 @@
* *
* @method void beforeLogin() #pw-hooker * @method void beforeLogin() #pw-hooker
* @method void afterLogin() #pw-hooker * @method void afterLogin() #pw-hooker
* @method void executeLogout() #pw-hooker * @method string executeLogout() #pw-hooker
* @method string executeLoggedOut() #pw-hooker
* @method string afterLoginOutput() #pw-hooker * @method string afterLoginOutput() #pw-hooker
* @method void afterLoginRedirect($url = '') #pw-hooker * @method void afterLoginRedirect($url = '') #pw-hooker
* @method string afterLoginURL($url = '') #pw-hooker * @method string afterLoginURL($url = '') #pw-hooker
@@ -33,6 +34,7 @@
* @method void loginSuccess(User $user) #pw-hooker * @method void loginSuccess(User $user) #pw-hooker
* @method array getBeforeLoginVars() #pw-hooker * @method array getBeforeLoginVars() #pw-hooker
* @method array getLoginLinks() #pw-hooker * @method array getLoginLinks() #pw-hooker
* @method string renderLoginLinks() #pw-hooker
* *
* *
*/ */
@@ -332,8 +334,8 @@ class ProcessLogin extends Process implements ConfigurableModule {
// fallback if nothing set // fallback if nothing set
return $this->afterLoginOutput(); return $this->afterLoginOutput();
} else if($this->wire('input')->urlSegmentStr() === 'logout') { } else if($input->urlSegmentStr() === 'logout') {
$session->redirect('../'); $session->location('../');
} }
$tfa = $this->getTfa(); $tfa = $this->getTfa();
@@ -469,6 +471,61 @@ class ProcessLogin extends Process implements ConfigurableModule {
return $user->name; return $user->name;
} }
/**
* Determine the after login URL and page, then populate to session to pick up after login
*
* @since 3.0.167
*
*/
protected function determineAfterLoginUrl() {
$input = $this->wire()->input;
$session = $this->wire()->session;
// if weve already done this then exit early
if($session->getFor($this, 'afterLoginPageId') !== null) return;
// ProcessPageView sets a loginRequestPageID variable to its session
// this is the ID of the page that access control blocked and sent user to login
$requestPageId = (int) $session->getFor('ProcessPageView', 'loginRequestPageID');
if(!$requestPageId || $requestPageId === $this->wire()->page->id) return;
// load the requested page
$requestPage = $this->wire()->pages->get($requestPageId);
if(!$requestPage->id) {
$session->setFor($this, 'afterLoginPageId', 0);
return;
}
if($requestPage->process) {
// admin: this is likely an admin page with a Process module
$process = (string) $requestPage->process;
if(!$process || $process === "ProcessLogin") return;
$className = $this->wire()->modules->getModuleClass($process, true);
if(!$className || !class_exists($className)) return;
$url = call_user_func_array("$className::getAfterLoginUrl", array($requestPage));
} else {
// front-end
$url = '';
}
if(empty($url)) {
$url = $session->getFor('ProcessPageView', 'loginRequestURL');
} else {
// common integer GET vars in admin to identify if present and not already in $url
foreach(array('id', 'modal') as $name) {
$value = $input->get($name);
if($value === null || !ctype_digit("$value")) continue; // not an integer
if(preg_match('/[&?]' . $name . '=/', $url)) continue; // already present
$url .= (strpos($url, '?') ? '&' : '?') . "$name=" . ((int) $value);
}
}
// set what we found to session
$session->setFor($this, 'afterLoginUrl', $url);
$session->setFor($this, 'afterLoginPageId', $requestPage->id);
}
/** /**
* Perform login and redirect on success * Perform login and redirect on success
* *
@@ -501,18 +558,20 @@ class ProcessLogin extends Process implements ConfigurableModule {
/** /**
* Log the user out * Log the user out
* *
* @return string
*
*/ */
public function ___executeLogout() { public function ___executeLogout() {
if($this->logoutURL) { if($this->logoutURL) {
$url = $this->logoutURL; $url = $this->logoutURL;
} else if($this->isAdmin || $this->wire('page')->template == 'admin') { } else if($this->isAdmin || $this->wire('page')->template == 'admin') {
$url = $this->config->urls->admin; $url = $this->config->urls->admin . './?loggedout=1';
$this->message($this->labels('logged-out'));
} else { } else {
$url = "./?logout=2"; $url = "./?logout=2";
} }
$this->session->logout(); $this->session->logout();
$this->session->redirect($url, false); $this->session->redirect($url, false);
return '';
} }
@@ -530,12 +589,15 @@ class ProcessLogin extends Process implements ConfigurableModule {
$beforeLoginVars = $this->getBeforeLoginVars(); $beforeLoginVars = $this->getBeforeLoginVars();
$session->setFor($this, 'beforeLoginVars', $beforeLoginVars); $session->setFor($this, 'beforeLoginVars', $beforeLoginVars);
// any remaining checks only if currently in the admin // check if Process module provides an after login URL that it wants us to use
if(!$this->isAdmin) return; $this->determineAfterLoginUrl();
// if checks already completed don't run them again // if checks already completed don't run them again
if($session->getFor($this, 'beforeLoginChecks')) return; if($session->getFor($this, 'beforeLoginChecks')) return;
// any remaining checks only if currently in the admin
if(!$this->isAdmin) return;
if( ini_get('session.save_handler') == 'files' if( ini_get('session.save_handler') == 'files'
&& !$this->wire('modules')->isInstalled('SessionHandlerDB') && !$this->wire('modules')->isInstalled('SessionHandlerDB')
&& !$this->wire('input')->get('db') && !$this->wire('input')->get('db')
@@ -796,10 +858,13 @@ class ProcessLogin extends Process implements ConfigurableModule {
*/ */
protected function ___afterLoginRedirect($url = '') { protected function ___afterLoginRedirect($url = '') {
$url = $this->afterLoginURL($url); $url = $this->afterLoginURL($url);
/** @var Session $session */ $session = $this->wire()->session;
$session = $this->wire('session');
$session->removeFor($this, 'beforeLoginVars'); $session->removeFor($this, 'beforeLoginVars');
$session->removeFor($this, 'beforeLoginChecks'); $session->removeFor($this, 'beforeLoginChecks');
$session->removeFor($this, 'afterLoginUrl');
$session->removeFor($this, 'afterLoginPageId');
$session->removeFor('ProcessPageView', 'loginRequestPageID');
$session->removeFor('ProcessPageView', 'loginRequestURL');
$session->redirect($url, false); $session->redirect($url, false);
} }
@@ -815,32 +880,42 @@ class ProcessLogin extends Process implements ConfigurableModule {
*/ */
public function ___afterLoginURL($url = '') { public function ___afterLoginURL($url = '') {
$session = $this->wire()->session;
$afterLoginUrl = $session->getFor($this, 'afterLoginUrl');
$id = (int) $session->getFor($this, 'afterLoginPageId');
if($afterLoginUrl) {
$page = $id ? $this->wire()->pages->get($id) : new NullPage();
if(!$page->id || $page->viewable()) return $afterLoginUrl;
}
if(empty($url)) { if(empty($url)) {
/** @var User $user */ $user = $this->wire()->user;
$user = $this->wire('user');
if($this->loginURL) { if($this->loginURL) {
$url = $this->loginURL; $url = $this->loginURL;
} else if($this->isAdmin && $user->isLoggedin() && $user->hasPermission('page-edit')) { } else if($this->isAdmin && $user->isLoggedin() && $user->hasPermission('page-edit')) {
if($this->id || $this->wire('process') !== $this->className()) { if($this->id || ((string) $this->wire()->process) !== $this->className()) {
$url = './'; $url = './';
} else { } else {
$url = $this->wire('config')->urls->admin . 'page/'; $url = $this->wire()->config->urls->admin . 'page/';
} }
} else { } else {
$url = './'; $url = './';
} }
} }
$beforeLoginVars = $this->wire('session')->getFor($this, 'beforeLoginVars'); $beforeLoginVars = $session->getFor($this, 'beforeLoginVars');
if(!is_array($beforeLoginVars)) $beforeLoginVars = array(); if(!is_array($beforeLoginVars)) $beforeLoginVars = array();
if(!isset($beforeLoginVars['login'])) $beforeLoginVars['login'] = 1; if(!isset($beforeLoginVars['login'])) $beforeLoginVars['login'] = 1;
$url .= (strpos($url, '?') ? '&' : '?'); $a = array();
foreach($beforeLoginVars as $name => $value) { foreach($beforeLoginVars as $name => $value) {
if(strpos($url, "?$name=") !== false || strpos($url, "&$name=") !== false) continue; // skip if overridden if(strpos($url, "?$name=") !== false || strpos($url, "&$name=") !== false) continue; // skip if overridden
if(!is_int($value)) $value = $this->wire('sanitizer')->entities($value); $a[$name] = $value;
$url .= "$name=$value&"; }
if(count($a)) {
$url .= strpos($url, '?') !== false ? '&' : '?';
$url .= http_build_query($a);
} }
$url = rtrim($url, '&');
return $url; return $url;
} }
@@ -855,11 +930,11 @@ class ProcessLogin extends Process implements ConfigurableModule {
* *
*/ */
public function ___getBeforeLoginVars() { public function ___getBeforeLoginVars() {
$session = $this->wire('session'); $session = $this->wire()->session;
$vars = $session->getFor($this, 'beforeLoginVars'); $vars = $session->getFor($this, 'beforeLoginVars');
if(!is_array($vars)) $vars = array(); if(!is_array($vars)) $vars = array();
$id = $this->wire('input')->get('id'); $id = $this->wire()->input->get('id');
if($id !== null) $vars['id'] = $this->wire('sanitizer')->intUnsigned($id); if($id !== null) $vars['id'] = $this->wire()->sanitizer->intUnsigned($id);
return $vars; return $vars;
} }