 | Tor, so far
Up to now, you have used CakePHP to create a simple application for managing
products and dealers. In Part 1, you gained an understanding of the MVC paradigm.
In Part 2, you used the powerful component of scaffolding to easily develop a
framework for your application. You finished Part 3 with a number of projects to
improve Tor. The first was to sanitize your data.
What to sanitize
When you sanitized your data, you probably noticed that most of the user input so far
is fairly simple. Most of the data input can be filtered using the paranoid method, since you should not get anything too complex from the user. The login action of the users controller is shown below.
Listing 1. Sanitizing the username input
function login()
{
if ($this->data)
{
$results = $this->User->findByUsername(Sanitize::paranoid($this->data
['User']['username']));
if ($results && $results['User']['password'] == md5($this->data
['User']['password']))
{
$this->Session->write('user', $this->data['User']['username']);
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->set('error', true);
}
}
}
|
Similarly for registration, you would expect that a user's name should only contain letters, spaces, hyphens, and apostrophes. However, apostrophes and hyphens can be bad news for a SQL database. Most of the normal SQL injection-type cleaning is handled by the DBO layer, but you can use the clean method to perform heavy data sanitizing on other inputs, should you wish.
Listing 2. You should sanitize different inputs based on their expected values
function register()
{
if (!empty($this->data))
{
$this->data['User']['username'] = Sanitize::paranoid($this->data
['User']['username']);
$this->data['User']['email'] = Sanitize::paranoid($this->data
['User']['email'], array('@', '.', '-', '+'));
$this->data['User']['first_name'] = Sanitize::sql($this->data
['User']['first_name']);
$this->data['User']['last_name'] = Sanitize::sql($this->data
['User']['last_name']);
$this->data['User']['password'] = md5($this->data
['User']['password']);
|
These are just a couple of examples of how you could have sanitized your data.
Securing the application
Your next task was to secure Tor using the Security component. If you consider what forms need to have the most security, a natural guess would be any forms which change the database. This is a good rule of thumb: If a change is made to the database, the form should be submitted via the POST method.
One action that fits this description is the product delete action. Since deleting a
product removes a row from the database, it should be a POST-only request. The code to require this is shown below.
Listing 3. ProductsController
requiring POST for delete action
function beforeFilter()
{
$this->Security->requireAuth('delete');
$this->Security->requirePost('delete');
}
|
If you try deleting a product now, you will notice that you get a 400 error. This is
good because it means that any delete has to occur because of a particularly formatted
request. However, we need to get the delete functionality back, so you'll need to make
an appropriate change on your views that point to the delete
action. See Listing 4 for how the new showDelete section could look.
Listing 4. The delete action calls should happen inside forms
<?php if ($showDelete) { ?>
<?php echo $form->create('Product', array('action' => 'delete/' .
$product['Product']['id']));?>
<li><?php echo $form->end('Delete product');?></li>
<?php } ?>
|
Giving feedback for invalid requests
Your final task was to use the blackHoleCallback to provide
the user with an appropriate response from an invalid request. Its purpose is to
provide some helpful and friendly feedback, instead of a nasty server error.
One possible implementation for this code is provided below.
Listing 5. Friendlier information for a bad request
function beforeFilter()
{
$this->Security->requireAuth('delete');
$this->Security->requirePost('delete');
$this->Security->blackHoleCallback='invalid';
}
function invalid() {
header('HTTP/x 400 Bad Request');
echo('<h1>Tor</h1>');
echo('<p>We\'re sorry - there has been a problem processing your request.
Please try submitting the form again.</p>');
die;
}
|
Changing the default layout
You will be adding a favorite-products feature to Tor. The visitors to your site should
be able to save products for later or use an RSS feed to keep track of new products as
they become available. To add this functionality, you will be dealing with the
session-handling function and Request Handler component. However, before you can begin, you need
to expand Tor a bit to give it a place for its new features. To this end, you will
change the default layout to customize the look and feel of Tor.
When you created the products controller for Tor, you used the Bake script to
create the application framework. The end result was a generic page with a single table
on it that looked like Figure 1.
Figure 1. Plain scaffolding
While this scaffolding is good for solidifying your application design, you are now at
the point where users are going to need more functionality than simply viewing
products. Since the default layout provided a good start, all you have to do is update
the existing views a bit to organize the application.
Layouts
Layouts are a kind of view that do not correspond directly with a controller or model. Simply put, a layout is a template. Changing the default layout affects the area surrounding all of the other views and would commonly be used for things like headers, footers, and menu bars.
We have been utilizing the default CakePHP layout, which lives in
cake/libs/view/layouts/default.ctp (don't change it!). To override the layout
with our own custom template, copy that default template to
app/views/layouts/default.ctp and modify until it looks like Listing 6 (nothing after the menu div changes from the default template).
Listing 6. Overriding the layout with our own custom template
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Tor : <?php echo $title_for_layout;?>
</title>
<?php echo $html->charset();?>
<link rel="icon" href="<?php echo $this->webroot;?>favicon.ico"
type="image/x-icon" />
<link rel="shortcut icon" href="<?php echo $this->webroot;?>
favicon.ico" type="image/x-icon" />
<?php echo $html->css('cake.generic');?>
<?php echo $scripts_for_layout;?>
</head>
<body>
<div id="container">
<div id="header">
<h1><?php echo $html->link('Tor', '/'); ?>
: Welcome <?php echo $session->read('user') ?></h1>
</div>
<div id="content">
<div id="menu">
<?php echo $html->link('Products',
array('controller' => 'products')); ?> | <?php echo
$html->link('Favorites', array('controller' => 'users',
'action' => 'favorites')); ?> |
<?php echo $html->link('Login', array('controller' =>
'users', 'action' => 'login')); ?> |
<?php echo $html->link('Logout', array('controller' =>
'users', 'action' => 'logout')); ?>
</div>
...
(message flash, content for layour, cake link, debug, etc
...
</body>
</html>
|
This is code is based on the default layout for CakePHP, and simply adds a menu bar and
adds some Tor branding. Since it still uses the default CSS, the site will not change very much visually.
Notice that the layout is XHTML Transitional. Even though all of our views are in
XHTML, it is entirely possible that they be plugged into a layout in another format,
such as Atom, WML, or XHTML-Mobile. We will return to the idea of layouts when we add this functionality to the site.
|  |