Posts tagged 'example'

Login example with Zend_Auth

published on January 05, 2009.
Heads-up! You're reading an old post and the information in it is quite probably outdated.

Happy New Year! Hope everyone had a blast for New Year’s Eve and managed to get some rest :) This is my first working day for this year. I’m still kinda lazy and sleepy. And I wanna eat something all the time. Damn you candies!!!

So, here’s what I’m going to do: authenticate an user against a database table using Zend Framework’s Zend_Auth component. It’s really a piece of cake. You can see a working example here: http://robertbasic.com/dev/login/. Feel free to test it and report any misbehavior down in the comments. In the codes below all paths, class names, actions, etc. will be as are in the example, so you probably will need to changed those according to your setup.

Preparation

Because I’m gonna use a database, be sure to have set the default database adapter in the bootstrap file, I have it setup like this:

<?php
$config = new Zend_Config_Ini('../application/dev/config/db_config.ini', 'offline');
$registry = Zend_Registry::getInstance();
$registry->set('db_config',$config);
$db_config = Zend_Registry::get('db_config');
$db = Zend_Db::factory($db_config->db);
Zend_Db_Table::setDefaultAdapter($db);

I’ll need it later in the code. The table structure is as follows:

--
-- Table structure for table `zendLogin`
--

CREATE TABLE `zendLogin` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL,
  `password` varchar(32) NOT NULL,
  `name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

The login controller

The magic happens in the LoginController. It has two actions: indexAction and logoutAction. The indexAction will take care of showing the login form and processing the login process. The logoutAction will just logout the user. You would never figure out that one on your own, right?

Now, let’s get to the fun part — the code:

<?php
class Dev_LoginController extends Zend_Controller_Action
{
    public function indexAction()
    {
        // If we're already logged in, just redirect
        if(Zend_Auth::getInstance()->hasIdentity())
        {
            $this->_redirect('dev/secured/index');
        }

        $request = $this->getRequest();
        $loginForm = $this->getLoginForm();

        $errorMessage = "";

Not much happening here: if the user is already logged in, I don’t want him at the login form, so just redirect him somewhere else; most likely to a home page or a control panel’s index page.

The Zend_Auth implements the Singleton pattern — if you’re not familiar with it read http://framework.zend.com/manual/en/zend.auth.html#zend.auth.introduction and http://www.php.net/manual/en/language.oop5.patterns.php (at php.net scroll down to the example #2).

So, I’m just asking the Zend_Auth does it have an user identity stored in it; the identity gets stored only upon successful log in. I’m also getting the request object. The getLoginForm() is a function that I wrote for assembling the login form and is a part of the LoginController, I’ll show it’s code later.

<?php
if($request->isPost())
{
    if($loginForm->isValid($request->getPost()))
    {
        // get the username and password from the form
        $username = $loginForm->getValue('username');
        $password = $loginForm->getValue('password');

This doesn’t needs a lot of explanation: if it’s a post request, it means the form is submitted. If the submitted data is valid, just get the wanted values from the form.

<?php
        $dbAdapter = Zend_Db_Table::getDefaultAdapter();
        $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);

        $authAdapter->setTableName('zendLogin')
                    ->setIdentityColumn('username')
                    ->setCredentialColumn('password')
                    ->setCredentialTreatment('MD5(?)');

Here I’m getting the default database adapter, so I know whit which database I’m working with. Then I’m creating an adapter for Zend_Auth, which is used for authentication; the docs give good explanation on the adapter, read it here: http://framework.zend.com/manual/en/zend.auth.html#zend.auth.introduction.adapters.

Next, I’m telling the authentication adapter which table to use from the database, and which columns from that table. Also, I’m telling it how to treat the credentials — the passwords are stored as MD5 hashes, so the submitted passwords will first be MD5ed and then checked.

<?php
        // pass to the adapter the submitted username and password
        $authAdapter->setIdentity($username)
                    ->setCredential($password);

        $auth = Zend_Auth::getInstance();
        $result = $auth->authenticate($authAdapter);

I’m passing to the adapter the user submitted username and password, and then trying to authenticate with that username and password.

<?php
        // is the user a valid one?
        if($result->isValid())
        {
            // get all info about this user from the login table
            // ommit only the password, we don't need that
            $userInfo = $authAdapter->getResultRowObject(null, 'password');

            // the default storage is a session with namespace Zend_Auth
            $authStorage = $auth->getStorage();
            $authStorage->write($userInfo);

            $this->_redirect('dev/secured/index');
        }

If the user is successfully authenticated, get all information about him from the table (if any), like the real name, E-mail, etc. I’m leaving out the password, I don’t need that. Next I’m getting the Zend_Auth’s default storage and storing in it the user information. In the end I’m redirecting it where I want it.

<?php
else
{
    $errorMessage = "Wrong username or password provided. Please try again.";
}
}
}
$this->view->errorMessage = $errorMessage;
$this->view->loginForm = $loginForm;
}

And this is the end of the indexAction. I know I could take the correct message from $result with getMessages(), but I like more this kind of message, where I’m not telling the user which part did he got wrong.

<?php
public function logoutAction()
{
    // clear everything - session is cleared also!
    Zend_Auth::getInstance()->clearIdentity();
    $this->_redirect('dev/login/index');
}

This is the logoutAction. I’m clearing the identity from Zend_Auth, which is also clearing all data from the Zend_Auth session namespace. And, of course, redirecting back to the login form.

<?php
protected function getLoginForm()
{
    $username = new Zend_Form_Element_Text('username');
    $username->setLabel('Username:')
            ->setRequired(true);

    $password = new Zend_Form_Element_Password('password');
    $password->setLabel('Password:')
            ->setRequired(true);

    $submit = new Zend_Form_Element_Submit('login');
    $submit->setLabel('Login');

    $loginForm = new Zend_Form();
    $loginForm->setAction('/dev/login/index/')
            ->setMethod('post')
            ->addElement($username)
            ->addElement($password)
            ->addElement($submit);

    return $loginForm;
}

As promised, here’s the code for getLoginForm function. That’s the whole LoginController code, not really a rocket science :) Sorry if it’s a bit hard to keep up with the code, I needed it to break it up in smaller pieces…

And here’s the view script for the indexAction.

<?php
<h2>Zend_Login example</h2>

<p>
Hello! This is an example of authenticating users with the Zend Framework...
</p>

<p>Please login to proceed.</p>

<?php if($this->errorMessage != ""): ?>
<p class="error"><?= $this->errorMessage; ?></p>
<?php endif; ?>

<?= $this->loginForm; ?>

Other controllers

Couldn’t come up with a better subtitle :(

Here’s an example how to require the user to log in to see the page: in the init() method ask Zend_Auth is the user logged in, and if not redirect him to the login form. This way the user will have to log in to the “whole controller”. Implement the same only to the indexAction, and the user will have to only log in to see the index page; he’ll be able to access another page without logging in.

<?php
class Dev_SecuredController extends Zend_Controller_Action
{
    function init()
    {
        // if not logged in, redirect to login form
        if(!Zend_Auth::getInstance()->hasIdentity())
        {
            $this->_redirect('dev/login/index');
        }
    }

    public function indexAction()
    {
        // get the user info from the storage (session)
        $userInfo = Zend_Auth::getInstance()->getStorage()->read();

        $this->view->username = $userInfo->username;
        $this->view->name = $userInfo->name;
        $this->view->email = $userInfo->email;
    }

    public function anotherAction()
    {
    }
}

I’m also reading out the user information from the Zend_Auth’s storage, that I have stored there during the log in process.

So there. A fully working login system, which can be setup in a really short time.

Update: If you want, you can get an example source code from here: zendLogin.zip ~8kB

Happy hacking!

Styling the default Zend_Form layout

published on December 22, 2008.
Heads-up! You're reading an old post and the information in it is quite probably outdated.

Here’s an example for styling Zend_Form’s default layout. The default layout is using definition lists. While there’s an option for changing the default layout, the wrapper tags and stuff, I see no reason for it. Create the form, add some CSS and your good to go :)

Note: Be sure to provide a Document Type in your view scripts like this:

<?php
<?= $this->doctype('XHTML1_STRICT') ?>

because when the form is generated, ZF is looking at the doctype to see how to create the form elements. Forgetting the doctype will probably generate invalid markup. I learned the hard way. Don’t do the same mistake, k? :)

The generated markup

So, here’s what Zend_Form makes for us (this markup is after submitting the form, but whit generated error, to show the error markup, too):

<form enctype="application/x-www-form-urlencoded" method="post" action="">
<dl class="zend_form">
    <dt>
        <label for="input1" class="required">Input field #1:</label>
    </dt>
    <dd>
        <input type="text" name="input1" id="input1" value="" />
        <ul class="errors">
            <li>Value is empty, but a non-empty value is required</li>
        </ul>
        <p class="description">Description? Yes, please.</p>
    </dd>
    <dt>
        &nbsp;
    </dt>
    <dd>
        <input type="submit" name="submit" id="submit" value="Submit form" />
    </dd>
</dl>
</form>

The PHP code which generates this form (without the error, of course) goes like this:

<?php
$input1 = new Zend_Form_Element_Text('input1');
$input1->setLabel('Input field #1:')
          ->setDescription('Description? Yes, please.')
          ->setRequired(true);

$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Submit form')

$form = new Zend_Form();
$form->setMethod('post')
       ->addElement($input1)
       ->addElement($submit);

Now, the generated form looks kinda good with no styling (which is good, if some maniac comes to visit with CSS support disabled).

OK, I lie: there’s a minimum of CSS for setting the background to white and the width to 460 pixels.

As you can see I’ve shortened the HTML and the PHP in the example codes…

The styling

I like my forms a bit different: form elements and their labels side by side with element descriptions and eventual errors showing up under the element. Here’s the CSS to achieve this:

.zend_form{
background:#fff;
width:460px;
margin:5px auto;
padding:0;
overflow:auto;
}

.zend_form dt{
padding:0;
clear:both;
width:30%;
float:left;
text-align:right;
margin:5px 5px 5px 0;
}

.zend_form dd{
padding:0;
float:left;
width:68%;
margin:5px 2px 5px 0;
}

.zend_form p{
padding:0;
margin:0;
}

.zend_form input, .zend_form textarea{
margin:0 0 2px 0;
padding:0;
}

.submit{
float:right;
}

.required:before{content:'* '}

.optional:before{content:'+ '}

Of course, this CSS takes care only of the layout; things like font types and sizes, colors, borders, backgrounds, etc. are not essential for this.

So, with this CSS applied to the generated Zend_Form, you can see on the image what will come up. And you know what’s the best part? It’s good for Firefox, Internet Explorer 6, Chrome and Opera, both under Windows and GNU/Linux (sorry, not tested for Internet Explorer 7 and Safari, but they should play along as well).

I almost forgot: I added a class=“submit” to the submit button, to be able to float it right. I first tried to do that with input[type=submit], but IE doesn’t know that, and as I wanted to make a styling that looks (almost) the same in all browsers with no hacks, I decided to add the class attribute.

So there, this little CSS code snippet should get you started with styling your Zend Form’s.

Cheers!

Data filtering with PHP's Filter extension

published on December 15, 2008.
Heads-up! You're reading an old post and the information in it is quite probably outdated.

Today I was catching up on feeds and one of the articles lead me to GoPHP5.org, where I spent some time lurking. In the FAQ section of that site one sentence made me curios:

The Filter extension is a new security component in PHP.

Filter extension? Maybe it’s nothing new for some of you, but it is for me. I’ve never heard of it before. So I quickly hopped over to PHP.net and the Filter chapter of the manual.

The filter extension is an extension that comes by default in PHP 5.2. It is here to help us to “validate and filter data that comes from insecure sources, such as user input”. It can validate integers, booleans, floats, regular expressions, URLs, E-Mails and IPs. It can sanitize strings, integers, floats, URLs, E-Mails…

Examples

Here are some examples about what this extension is capable of. Lets assume that we get some data from a form with POST method. The 3 input fields are name, email and age (I’m not creating a real validator, but var_dump-ing the results of the filtering, to show what filter gives what kind of output).

<?php
// $_POST['name'] = "Robert <b>hello</b>";
var_dump(filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING));
// Output: string(12) "Robert hello"

// $_POST['email'] = "mail@example.com";
var_dump(filter_input(INPUT_POST, 'name', FILTER_VALIDATE_EMAIL));
// Output: string(16) "mail@example.com"

// $_POST['age'] = "22";
var_dump(filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT,
                        array('options' => array('min_range' => 18,
                                                'max_range' => 28)
                        )));
// Output: int(22)

With the first filter I’m using the FILTER_SANITIZE_STRING which strips down all tags and unwanted characters from our string. The second filter validates the provided E-mail address: pass it a malformed E-mail address and it will result with a boolean false. The third filter validates the age: it must be an integer and in the range between 18 and 28 (the min and max ranges are optional, I added them just for the example).

Besides input filtering it can filter variables, too:

<?php
$string = "Some funky string with <b>html</b> code and 'quotes'";
var_dump(filter_var($string, FILTER_SANITIZE_STRING));
// Output: string(53) "Some funky string with html code and 'quotes'"
// NOTE: the single quotes in the output are encoded as &amp;#39;

var_dump(filter_var($string, FILTER_SANITIZE_MAGIC_QUOTES));
// Output: string(54) "Some funky string with html code and \'quotes\'"
// NOTE: the <b></b> html tags are NOT stripped in the output

var_dump(filter_var($string, FILTER_SANITIZE_ENCODED));
// Output: string(80) "Some%20funky%20string%20with%20%3Cb%3Ehtml%3C%2Fb%3E%20code%20and%20%27quotes%27"

Play around with it, get familiar, cause this is one nice extension that will help you make more secure web sites and web apps.

Cheers!

MyUrl view helper for Zend Framework

published on December 02, 2008.
Heads-up! You're reading an old post and the information in it is quite probably outdated.

I started writing some boring introduction but I’ll just skip to the point.

The problem

Zend Framework’s built in URL view helper — Zend_View_Helper_Url — is discarding the query string of the URL, thus breaking some links.

Example: If I’m on a page like:
http://project/foo/bar/?param1=value1
and in the bar.phtml I use the Url helper like this:

<?php
<?= $this->url(array('param2' => 'value2')); ?>

I expect this:
http://project/foo/bar/param2/value2/?param1=value1
or something similar to this. This would be just perfect:
http://project/foo/bar/param1/value1/param2/value2
But no, it gives:
http://project/foo/bar/param2/value2/

The solution

After working on several workarounds, currently this is the best one I can think of — take the link that is created by the built-in Url helper and add the query string on that link:

<?php

// Usage:
// <?= $this->myUrl($this->url(array('param2' => 'value2'))); ?>
// Output:
// http://project/controller/action/param2/value2/?param1=value1
class Zend_View_Helper_MyUrl
{
    public function myUrl(&$url, &$toAdd = array())
    {
        $requestUri = Zend_Controller_Front::getInstance()->getRequest()->getRequestUri();
        $query = parse_url($requestUri, PHP_URL_QUERY);
        if($query == '')
        {
            return $url;
        }
        else if(empty($toAdd))
        {
            return $url . '/?' . $query;
        }
        else
        {
            $toAdd = (array)$toAdd;
            $query = explode("&", $query);

            $add = '/?';

            foreach($toAdd as $addPart)
            {
                foreach($query as $queryPart)
                {
                    if(strpos($queryPart, $addPart) !== False)
                    {
                        $add .= '&' . $queryPart;
                    }
                }
            }
            return $url . $add;
        }
    }
}

The second parameter, $toAdd, should be an array of parameters that we want to add to the URL. Say, if I have a query string like:
?param1=value1&someotherparam=anditsvalue
but want only to add the param1=value1 to the URL, I would pass “param1” as the second parameter. Not passing anything as the second parameter will result in adding the complete query string to the URL.

This is an ugly hack to make ugly links work, but it works. Thoughts?

Cheers!

A Zend_Captcha example

published on October 22, 2008.
Heads-up! You're reading an old post and the information in it is quite probably outdated.

Update: I made an error in the example code, regarding the CAPTCHA image URL. I’m sorry for any troubles caused by this mistake.

Update #2: Here’s an example of using Zend_Captcha without the whole Zend Framework stuff.

Update #3: There was an unintentional error in the captchaAction() method, Adam warned me about it in the comments. The error is fixed now. Thanks Adam.

OK, this was a bit tricky and I found no examples about it, so I thought to blog it. I’ll just show a quick example how to implement Zend_Captcha into a Zend_Form, may be useful for someone. There are several CAPTCHA types in ZF, like the Image, Figlet and Dumb. I use Image.

First of all, we’ll use sessions, so we need to change the bootstrap file a little:

<?php
// Put this line somewhere after the Zend_Loader::registerAutoload(); line
Zend_Session::start();

We need to start the session to use it, putting it close to the top will assure that there will be no “Headers already sent by…” errors caused by a wrongly placed session start.

Next we need a folder which has a 777 permission on it (Windows users, you can skip this… Or start using GNU/Linux) where we will put our captcha images for a while… This folder must be in the public folder somewhere. So create one.

How does this work? When a captcha is generated, it generates a unique ID (e.g. 539e517b0c0f4e32ef634dae92f07f77) and the word on the image. That unique ID is used for the file name of the image and for the session namespace (the namespace is like: Zend_Form_Captcha_uniqueId), so it knows which image belongs to which session. Also, the generated word is placed inside it’s own session. That ID is placed on the form in a hidden field, so when the submission is received, we can access the ID and recreate the correct session namespace and access the data in it: the word on the image.

Awesome. Now, to the fun part. I use the Zend_Form_Element_Captcha class, so no additional fooling around is needed to put the captcha in the form. Here’s the code:

<?php
public function indexAction()
{
// Our form object...
$form = new Zend_Form();
// And here's our captcha object...
$captcha = new Zend_Form_Element_Captcha(
        'captcha', // This is the name of the input field
        array('label' => 'Write the chars to the field',
        'captcha' => array( // Here comes the magic...
        // First the type...
        'captcha' => 'Image',
        // Length of the word...
        'wordLen' => 6,
        // Captcha timeout, 5 mins
        'timeout' => 300,
        // What font to use...
        'font' => '/path/to/font/FontName.ttf',
        // Where to put the image
        'imgDir' => '/var/www/project/public/captcha/',
        // URL to the images
        // This was bogus, here's how it should be... Sorry again :S
        'imgUrl' => 'http://project.com/captcha/',
)));
// Add the captcha element to the form...
$form->setAction('/index/captcha/')
        ->setMethod('post')
        // Add the captcha to the form...
        ->addElement($captcha)
        ->addElement('submit','Submit')
// Pass the form to the view...
$this->view->form = $form;
}

On the other side, it goes something like this:

<?php
public function captchaAction()
{
  $request = $this->getRequest();
  // Get out from the $_POST array the captcha part...
  $captcha = $request->getPost('captcha');
  // Actually it's an array, so both the ID and the submitted word
  // is in it with the corresponding keys
  // So here's the ID...
  $captchaId = $captcha['id'];
  // And here's the user submitted word...
  $captchaInput = $captcha['input'];
  // We are accessing the session with the corresponding namespace
  // Try overwriting this, hah!
  $captchaSession = new Zend_Session_Namespace('Zend_Form_Captcha_'.$captchaId);
  // To access what's inside the session, we need the Iterator
  // So we get one...
  $captchaIterator = $captchaSession->getIterator();
  // And here's the correct word which is on the image...

  $captchaWord = $captchaIterator['word']
  // Now just compare them...
  if($captchaInput == $captchaWord)
  {
  // OK
  }
  else
  {
  // NOK
  }
}

Easy, ain’t it?

Happy hacking :)

Tip: Using a monospace or a serif font for the words on the image (like FreeMono.ttf found by default on Ubuntu), makes the word quite unreadable — with the FreeMono.ttf about 8 out of 10 is UNreadable — so use a sans-serif font.

Robert Basic

Robert Basic

Software engineer, consultant, open source contributor.

Let's work together!

If you require outsourcing or consulting help on your projects, I'm available!

Robert Basic © 2008 — 2019
Get the feed