Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The initial handling of form data within the code was quite insecure, and several areas of the site are still this way. After learning more about web security, I developed a security model based on "continuations". All of the pages have been converted to using continuations.

Wiki MarkupDeep linking into the site is only allowed for modes in the $actions\['entry'\] array in states.php. Anything else requires the submission of a continuation. For the most part, access to different parts of the site is controlled by what privileges you have in the Privileges section of the site. However, there are a few things controlled by a user's adminlevel field in the user table. The very earliest form of authorization was handled by the user's adminlevel field, and it has continued to be useful in a few situations.

All form data passed in to the site should be verified very strictly. Unfortunately, that is not currently the case. All of the main pages available to the average user should have been updated to have strict validation, though other parts of the site have not made it yet. Most sections of the site have a single function (or a very small number of similar functions) that handle the processing of form data. This will make it easier to add better checks throughout the site as the number of locations needing to be modified is fairly small.

...

First, modify states.php to add a new mode.

  1. Wiki Markupcreate a new $actions\['mode'\] with the name of your mode set to the name of the function that should be called Wiki Markup
  2. create a new $actions\['pages'\] with the name of your mode set to the name of the section this mode belongs to. This is only an internal identifier used to associate modes together.

So, if your mode is named "examplemode", you could end up with these two lines being added:

Code Block

$actions['mode']['examplemode'] = "exampleFunc1";
$actions['pages']['examplemode'] = "exampleSection";

...

While we're editing states.php, lets jump to the top and add our new mode to $actions\['entry'\] so that it can be called directly without having to already be on the site. Just add 'examplemode' as a new item at the end of the array.

The next thing to do is to actually add the functions. Lets place them in a new file called 'examples.php' in the .ht-inc directory. Our first function can be really simple and just print out some text. So, create examples.php with this in it:

Code Block

<?php
function exampleFunc1() {
   print "exampleFunc1 successfully called.<br>\n";
}
?>

There's one last thing we need to do before linking this in on the site. As described in the "General Processing Flow" section, initGlobals includes the required files based on the current mode's section. So, edit utils.php and scroll toward the end of it where files are included (using require_once) within a switch statement. In the switch statement, before the "default" case, add

Code Block

case 'exampleSection':
   require_once('.ht-inc/examples.php');
   break;

Now, we're ready to actually add a link for this example function to the navigation area (of course, not all modes are linked to from the navigation area, but it is an easy example). To do this, edit utils.php and find the getNavMenu function close to the bottom of the file. We'll add our new mode to the end; so, find the "logout" part which should look something like this:

Code Block

if($inclogout) {
   $rt .= menulistLI('authentication');
   $rt .= "<a href=\"" . BASEURL . SCRIPT . "?mode=logout\">";
   $rt .= "Logout</a></li>\n";
}

We'll basically duplicate that (without the if conditional), changing a few things so that we add this right below it:

Code Block

$rt .= menulistLI('exampleSection');
$rt .= "<a href=\"" . BASEURL . SCRIPT . "?mode=examplemode\">";
$rt .= "Example Section</a></li>\n";

...

So, change the contents of examplefunc1 to be:

Code Block

$options = array(0 => "option1",
                 1 => "option2");
print "<FORM action=\"" . BASEURL . SCRIPT . "\" method=post>\n";
print "Select one of these options:";
printSelectInput("theoption", $options);
$cont = addContinuationsEntry("submitFunc1Form", $options);
print "<INPUT type=hidden name=continuation value=\"$cont\">\n";
print "<INPUT type=submit value=Submit>\n";
print "</FORM>\n";

Now, we have a form that gets displayed when "Example Section" is clicked. Now, we need to add a function to process that form. Add this function to examples.php:

Code Block

function submitFunc1Form() {
   $data = getContinuationVar();
   $theoption = processInputVar("theoption", ARG_NUMERIC);
   if(! array_key_exists($theoption, $data)) {
      print "invalid option submitted\n";
      return;
   }
   print "The option you selected was: ";
   print "{$data\[$theoption\]}<br>\n";
}

Next, we add this function to states.php:

Code Block

$actions['mode']['submitFunc1Form'] = "submitFunc1Form";
$actions['pages']['submitFunc1Form'] = "exampleSection";

...

Add this to the end of examplefunc1:

Code Block

print "<br><br>\n";
print "<div id=examplediv>\n";
print "This will get dynamically changed.<br>\n";
print "</div>\n";
$cont = addContinuationsEntry("AJexample");
print "<a onclick=\"JS_AJexample('$cont');\">Click to ";
print "update</a><br>\n";

Next, we need to add the javascript function we just referenced to code.js (in .ht-inc's parent directory) as well as a callback function that will be called when the results of the AJAX call are returned:

Code Block

function JS_AJexample(contid) {
   dojo.xhrPost({
      url:'index.php',
      load: JS_AJexampleCB,
      error: errorHandler,
      content: {continuation: contid},
      timeout: 15000
   });
}

function JS_AJexampleCB(data, ioArgs) {
   eval(data);
}

...

to the $actions array:

Code Block

$actions['mode']['AJexample'] = "exampleFunc2";
$actions['pages']['AJexample'] = "exampleSection";

to the $noHTMLwrappers array:

Code Block
languagejava
"AJexample"
'AJexample'

Now, we need to create exampleFunc2 (in examples.php):

Code Block

function exampleFunc2() {
   print "document.getElementById('examplediv').innerHTML = 'Dynamic update';";
}

Then, we do something we haven't seen before. getDojoHTML (in utils.php) must be modified so that the correct dojo requires get set when we are in mode examplefunc1. For a simple AJAX update, we only need the dojo.parser module to be loaded. Since this is already loaded for some of the Group modes, just add another case statement under submitDeleteGroup so we have:

Code Block

case 'viewGroups':
case 'submitEditGroup':
case 'submitAddGroup':
case 'submitDeleteGroup':
case 'examplemode':
   $dojoRequires = array('dojo.parser');
   break;

We also have to add a case statement a little further down where the HTML is actually generated. Find "case 'submitDeleteGroup':" in the switch statement following the one we just modified, and add another case statement for examplemode so we have:

Code Block

case 'viewGroups':
case 'submitEditGroup':
case 'submitAddGroup':
case 'submitDeleteGroup':
case 'examplemode':
   $rt .= "<style type=\"text/css\">\n";

...

All of the code has been indented using tabs (rather than spaces) except where code wraps to more than one line, in which case a combination of tabs and spaces are used. This allows someone editing the code to set his tab width to whatever he prefers, but still allows code that wraps to multiple lines to line up correctly. To do this, when you want to wrap code to another line, put a newline at the end of the one you are on just as you normally would. Next, tab over from the beginning of the line until you are even with where the initial line of code started, then use spaces to line up this line with something in the line above it that makes sense. I'll give an example. Almost all of the queries are written on multiple lines like this:

Code Block

=====------------------------------
     $query = "SELECT id, "
            .        "name, "
            .        "prettyname "
            . "FROM image "
            . "WHERE id < 400 AND "
            .       "OSid = 3";
=====------------------------------

...

Online documentation of the code is generated using Doxygen. Each file that should be parsed to generate docs needs to have the following toward the top of it:

Code Block

/**
 * \file
 */

All functions should have a header above them that documents what it does. The header should include the name of the function, a description of any parameters passed in, a description of anything returned, and a brief description of what the function does. The format of the header is:

Code Block

/////////////////////////////////////////////////////////////////////
///
/// \fn nameOfFunction($param1, $param2)
///
/// \param $param1 - description of param1
/// \param $param2 - description of param2
///
/// \return description of datastructure returned
///
/// \brief short description of what the function does
///
/////////////////////////////////////////////////////////////////////

...