Changeset - 182b6be0813e
[Not reviewed]
default
0 2 0
Nathan Brink (binki) - 13 years ago 2012-11-09 22:59:20
ohnobinki@ohnopublishing.net
Fix the support for automatic WebAdvisor registration to not enter infinite loops.

The automatic registration process used to enter an infinite loop
every time it was used if the user had been previously issued a
TOKENIDX by WebAdvisor. This means that it would work the first time
in most cases but rarely a second time, if ever. Now we use
LASTTOKEN=NULL instead of TOKENIDX= to request a new token and it
works pretty well.
2 files changed with 42 insertions and 46 deletions:
0 comments (0 inline, 0 general)
scripts/webadvisor_tokenidx.js
Show inline comments
 
/* -*- tab-width: 4; -*-
 
 * Copyright 2010 Nathan Gelderloos, Ethan Zonca, Nathan Phillip Brink
 
 *
 
 * This file is part of SlatePermutate.
 
 *
 
 * SlatePermutate is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * SlatePermutate is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with SlatePermutate.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/*
 
 * Assumes that WebAdvisor_scripts.js for WebAdvisor-2.x is loaded,
 
 * displayFormHTML() or something was called and thus
 
 * readURLParameters() was called. We attempt to extract TOKENIDX and
 
 * asynchronously inform slate_permutate about it. We currently assume
 
 * we're on a login form too.
 
 */
 

	
 
var slate_permutate_input_login;
 

	
 
(function() {
 
		var slate_permutate_onload = function() {
 

	
 
				/*
 
				 * Override the login form's submission handler to catch the
 
				 * case where we're still trying to load the TOKENIDX or
 
				 * something else.
 
				 */
 
				var inputs = document.getElementsByTagName('input');
 
				for (var i = 0; i < inputs.length; i ++)
 
				{
 
						slate_permutate_input_login = inputs.item(i);
 
						if (slate_permutate_input_login.getAttribute('name') == 'SUBMIT2')
 
								break;
 
				}
 
				slate_permutate_input_login.setAttribute('value', 'Discovering TOKENIDX...');
 
				slate_permutate_input_login.setAttribute('disabled', 'disabled');
 

	
 
				/*
 
				 * Discover the TOKENIDX if it's available.
 
				 */
 
				var sp_err = document.getElementById('sp_err');
 
				if (containsParameter(g_tokenIdx))
 
				{
 
					/* Remove the warning about the script not having loaded */
 
					sp_err.replaceChild(document.createTextNode("Slate Permutate TOKENIDX-acquiring script loaded…"), sp_err.firstChild);
 
					sp_err.setAttribute('style', 'color: grey;');
 

	
 
					/* Inform home base of the newly generated TOKENIDX. */
 
						var TOKENIDX = getURLParameter(g_tokenIdx);
 
						var myscript = document.createElement('script');
 
						myscript.setAttribute('type', 'text/javascript');
 
						myscript.setAttribute('src', decodeURIComponent(getURLParameter('SP_CALLBACK')) + 'callback=slate_permutate_token_callback&TOKENIDX=' + TOKENIDX);
 
						document.getElementsByTagName('head').item(0).appendChild(myscript);
 
				}
 
				else
 
				{
 
						alert('Unable to discover TOKENIDX. You must register manually.');
 
					sp_err.replaceChild(document.createTextNode('Slate Permutate unable to acquire TOKENIDX. You must register manually.'), sp_err.firstChild);
 
					sp_err.setAttribute('style', 'color: red; background: yellow;');
 
						alert('Unable to discover WebAdvisor TOKENIDX. You must register manually.');
 
				}
 
		}
 

	
 
		/*
 
		 * Register to run after either of getWindowHTML(),
 
		 * setWindowHTML(), or displayFormHTML() have been run. These are
 
		 * run after onload="", so they are required if we're to wait for
 
		 * the DOM to load...
 
		 */
 
		var funcs = ['getWindowHTML', 'setWindowHTML', 'displayFormHTML'];
 
		for (var i = 0; i < funcs.length; i ++)
 
		{
 
				var func = window[funcs[i]];
 
				window[funcs[i]] = function() {
 
						func();
 
						slate_permutate_onload();
 
				};
 
		}
 
})();
 

	
 
function slate_permutate_token_callback(result)
 
{
 
		if (result)
 
		{
 
				slate_permutate_input_login.setAttribute('value', 'LOG IN');
 
				slate_permutate_input_login.removeAttribute('disabled');
 

	
 
			var sp_err = document.getElementById('sp_err');
 
			sp_err.replaceChild(document.createTextNode('Slate Permutate has acquired WebAdvisor TOKENIDX, ready for login.'), sp_err.firstChild);
 
			sp_err.setAttribute('style', 'color: green;');
 
		}
 
}
webadvisor.php
Show inline comments
 
@@ -18,48 +18,50 @@
 
 * along with SlatePermutate.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
require_once('inc/class.page.php');
 

	
 
/*
 
 * Handle the scripts/webadvisor_tokenidx.js making a TOKENIDX
 
 * callback, storing that TOKENIDX in our SESSION for later use.
 
 */
 
if (!empty($_GET['TOKENIDX']))
 
  {
 
    page::session_start();
 

	
 
    $_SESSION['webadvisor_TOKENIDX'] = $_GET['TOKENIDX'];
 

	
 
    $result = 'received ' . $_GET['TOKENIDX'];
 

	
 
    header('Content-Type: text/javascript; charset=utf-8');
 

	
 
    if ($jsonp = !empty($_GET['callback']))
 
      echo $_GET['callback'] . '(';
 
    echo json_encode($result);
 
    if ($jsonp)
 
      echo ");\n";
 
    if ($jsonp && !empty($_GET['destination']))
 
      echo 'document.location.href = ' . json_encode($_GET['destination']) . ";\n";
 
    exit;
 
  }
 

	
 
$page = page::page_create('WebAdvisor');
 
$school = $page->get_school();
 

	
 
if (empty($school['webadvisor_url']))
 
  {
 
    if (!empty($school['registration_url']) && preg_match(',(.*/WebAdvisor),', $school['registration_url'], $matches))
 
      $school['webadvisor_url'] = $matches[1];
 
    else
 
      $school['webadvisor_url'] = $school['url'] . 'WebAdvisor';
 
  }
 

	
 
/**
 
 * \brief
 
 *   Calculate the URI necessary for logging into WebAdvisor.
 
 *
 
 * \param $school
 
 *   The school.
 
 * \param $dest
 
 *   The URI to visit after the user has logged into WebAdvisor and
 
 *   the TOKENIDX has been communicated to $tokenidx_callback.
 
 * \param $tokenidx_callback
 
@@ -90,123 +92,86 @@ function webadvisor_login($page, array $
 
  $dest .= 'from_webadvisor';
 

	
 
  $webadvisor_login_func = $school['id'] . '_webadvisor_login';
 
  if (function_exists($webadvisor_login_func))
 
    $webadvisor_login_func($school, $dest);
 

	
 
  /*
 
   * The hack we are using is that somehow TOKENIDX=&SS=LGRQ&URL=<URI>
 
   * will both initialize the user's browser with a token cookie and
 
   * then redirect to URL. Trying to use the proper way of loading the
 
   * LGRQ (using TYPE=P&PID=UT-LGRQ&PROCESS=-XUTAUTH01) doesn't work
 
   * because it drops and ignores our URL parameter, leaving the user
 
   * at the KV site. No other URL I've fiddled with seems to be able
 
   * to do this combination of logging in and returning the user to us
 
   * or a URI of our choosing. Once the user's browser has been
 
   * initialized with a TOKENIDX, loading the page
 
   * SS=LGRQ&URL=<URI>&ERROR=<XSS> will preserve the ERROR=<XSS>
 
   * necessary for our XSS and insert it into the login page.
 
   *
 
   * HOWEVER, if the browser already has a TOKENIDX-related cookie,
 
   * then visiting TOKENIDX=&SS=LGRQ&URL=<URL> will cause WebAdvisor
 
   * to keep redirecting to itself infinitely. Similarly, if the
 
   * browser does not yet have a TOKENIDX-related cookie,
 
   * SS=LGRQ&URL=<URL> will redirect the user to URL without giving
 
   * the user a cookie. Thus, our strategy is:
 
   *
 
   * 1. Send the user to
 
   *    SS=LGRQ&URL=<URL>&SP_CALLBACK=<SP_CALLBACK>&ERROR=<XSS>. In
 
   *    this case, the URL will be set to have `from_webadvisor' as a
 
   *    GET parameter and ERROR will be set to the appropriate XSS for
 
   *    the normal login form. Thus, if the user does not have a
 
   *    token, he will be directed here and sent to step #2 to get a
 
   *    token. Otherwise, the user will have a jump start (already
 
   *    having TOKENIDX cookies) and communicate his token to us while
 
   *    logging in.
 
   *
 
   * 2. If webadvisor.php is called with from_webadvisor, that means
 
   *    one of two things. It might mean that webadvisor_tokenidx.js
 
   *    was called successfully and we have the webadvisor TOKENIDX
 
   *    stored in our session. In that case, the user's browser
 
   *    already had a WebAdvisor TOKENIDX before we did #1; also, this
 
   *    function won't be called in that case because this function is
 
   *    only called if TOKENIDX is unknown. Thus, we don't know the
 
   *    TOKENIDX, meaning that we need to request that the WebAdvisor
 
   *    installation allocate a TOKENIDX for the user and _then_
 
   *    proceed directly to the login page to send us TOKENIDX.
 
   * the user a cookie. Thus, our strategy is to specify
 
   * LASTTOKEN=NULL: this is the magic which prevents the silly
 
   * infinite looping when the user already has cookies and also works
 
   * when the user doesn’t have a cookie to start with.
 
   */
 

	
 
  $login_form_uri = $school['webadvisor_url'] . '?SS=LGRQ&URL=' . rawurlencode($dest)
 
  $login_form_uri = $school['webadvisor_url'] . '?LASTTOKEN=NULL&SS=LGRQ&URL=' . rawurlencode($dest)
 
    . '&SP_CALLBACK=' . rawurlencode($tokenidx_callback)
 
    . '&ERROR=' . rawurlencode('<script type="text/javascript" src="' . htmlentities(page::uri_resolve('scripts/webadvisor_tokenidx.js'), ENT_QUOTES) . '"></script>');
 

	
 
  if (isset($_GET['from_webadvisor']))
 
    /*
 
     * Case 2, infer that browser needs TOKENIDX cookies _and_ that
 
     * the following URI won't cause endless looping
 
     * (hopefully). Unfortunately, this process is not reentrant.
 
     */
 
    redir($school['webadvisor_url'] . '?TOKENIDX=&SS=LGRQ&URL=' . rawurlencode($login_form_uri));
 

	
 
  /*
 
   * Case 1, assume that the user has a TOKENIDX cookie _but_ make
 
   * provisions ($dest has from_webadvisor in it) for needing to
 
   * allocate that cookie.
 
   */
 
    . '&ERROR=' . rawurlencode('<script type="text/javascript" src="' . htmlentities(page::uri_resolve('scripts/webadvisor_tokenidx.js?20121110f'), ENT_QUOTES) . '"></script><span id="sp_err">Slate Permutate loading… (automatic registration may not be working)</span>');
 
  redir($login_form_uri);
 

	
 
  return array(
 
    /* 'preload' => $school['webadvisor_url'] . '?TYPE=P&PID=UT-LGRQ&PROCESS=-XUTAUTH01&URL=', */
 
    'uri' => $school['webadvisor_url'] . '?SS=LGRQ&URL=' . rawurlencode($login_form_uri),
 
  );
 
}
 

	
 
function redir($dest)
 
{
 
  header('HTTP/1.1 302 Found');
 
  header('Location: ' . $dest);
 
  header('Content-Type: text/plain; charset=utf-8');
 
  echo 'Location: ' . $dest;
 
  exit;
 
}
 

	
 
/*
 
 * If the page load was not a redirection from webadvisor, we must
 
 * clear our local cache of TOKENIDX's value. We need to get a new
 
 * token because we can't guess what SS= value the ST-WERG form will
 
 * take unless if we start with a new TOKENIDX which doesn't have any
 
 * SSes yet. Also, the old token may have (very likely) expired
 
 * because of the short login timeout.
 
 */
 
if (!isset($_GET['from_webadvisor']))
 
  unset($_SESSION['webadvisor_TOKENIDX']);
 

	
 
if (empty($_SESSION['webadvisor_TOKENIDX']))
 
  {
 
    /*
 
     * Get a token for the ST-WERG form and have the user perform the
 
     * WebAdmin-specific login. This can only be done after the login form
 
     * has an SS allocated for it.
 
     * WebAdvisor-specific login. This can only be done after the
 
     * login form has an SS allocated for it.
 
     */
 
    webadvisor_login($page, $school, page::uri_resolve('webadvisor.php') . '?r=' . rand()
 
		     . '&sections=' . rawurlencode(empty($_GET['sections']) ? '' : $_GET['sections'])
 
		     . '&school=' . rawurlencode($school['id']),
 
		     page::uri_resolve('webadvisor.php?'));
 
  }
 

	
 
/*
 
 * Use the hopefully-still-valid TOKENIDX to initialize an ST-WERG
 
 * (STudent Web[A]dvisor Express ReGistration) form. When that form is
 
 * iniailized, assume that it has SS=1 and submit the form. &APP=ST
 
 */
 
$TOKENIDX = $_SESSION['webadvisor_TOKENIDX'];
 
$page->head();
 
echo '<form id="sp-webadvisor-form" action="' . htmlentities($school['webadvisor_url'] . '?TOKENIDX=' . $TOKENIDX . '&SS=1', ENT_QUOTES) . '" method="post">' . PHP_EOL;
 
echo '<p>';
 

	
 
$uri = $school['webadvisor_url'] . '?TOKENIDX=' . $TOKENIDX . '&TYPE=P&PID=ST-WERG';
 
$onload_html = '="' . htmlentities('javascript:document.getElementById(\'sp-webadvisor-form\').submit()', ENT_QUOTES) . '"';
 
echo '  <img src="' . htmlentities($uri, ENT_QUOTES) . '" alt="Loading WebAdvisor Express Registration form (ST-WERG)…"'
 
. ' onload' . $onload_html . ' onerror' . $onload_html . ' />' . PHP_EOL;
 
echo '  If you are not redirected after 16 seconds, you may try: ' . PHP_EOL;
 

	
 
$sections = explode(',', empty($_GET['sections']) ? '' : $_GET['sections']);
0 comments (0 inline, 0 general)