Using ADODB with Zend_Auth and Zend_Auth_Adapter
Monday, 30. April 2007, 01:25:00
Good thing Zend employs smart people: they use an adapter to control the database access in the Zend_Auth class, called Zend_Auth_Adapter. You can define your own authentication adapter quite easily by creating a class which implements the Zend_Auth_Adapter_Interface interface class.
Let's look at how do you actually do this!
I assume that you know how to use Zend_Auth and ADODB already, so I won't go into much details about them now, just how to create the auth_adapter.
Let's begin
First, we need to define our authentication class. I call mine simply ADODBAuthAdapter:
<?php
require_once('Zend/Auth/Adapter/Interface.php');
class ADODBAuthInterface implements Zend_Auth_Adapter_Interface
{
public function __construct($db)
{
}
public function authenticate()
{
}
}
?>
This is the base for our class. Zend_Auth_Adapter_Interface defines one function, authenticate(), which we have in our class. We also define a skeleton for the constructor with one parameter, the ADODB database handle.
Now, we need a few variables to store things such as the database table and the columns.
<?php
require_once('Zend/Auth/Adapter/Interface.php');
class ADODBAuthInterface implements Zend_Auth_Adapter_Interface
{
private $db;
private $tableName;
private $nameColumn;
private $passColumn;
private $name;
private $password;
public function __construct($db)
{
$this->db = $db;
$this->tableName = 'users';
$this->nameColumn = 'login';
$this->passColumn = 'password';
}
public function authenticate()
{
}
}
?>
The variables should be quite obvious: the database table, the column for name, the column for password and variables to store the name and password the user is trying to auth with.
In the constructor we assign the database related variables some defaults.
Now we need a bunch of functions to assign values to the variables. The snippet is getting kind of long, so I will now just show the code for the functions, you can put it under the __construct function.
public function setTableName($table)
{
$this->tableName = $table;
return $this;
}
public function setIdentityColumn($nameCol)
{
$this->nameColumn = $nameCol;
return $this;
}
public function setCredentialColumn($passCol)
{
$this->passColumn = $passCol;
return $this;
}
public function setIdentity($name)
{
$this->name = $name
return $this;
}
public function setCredential($pass)
{
$this->password = $pass;
return $this;
}
I wanted to have similar function names as the adapter for Zend_DB, so that's where the names for the functions come from. But why do they return $this?
Well, it allows you to use them like this:
$myAdapter = new ADODBAuthAdapter($db);
$myAdapter->setIdentity('myname')
->setCredential('mypass');
You may have noticed some other Zend Framework classes can be used like that too.
Now we have everything else except the authenticate function done. The it's the authenticate functions job to query the database and parse the results and after that it has to return an instance of Zend_Auth_Result.
Again, just the function. Replace the empty authenticate with this code.
public function authenticate()
{
$sql = 'SELECT '.$this->nameColumn.'
FROM '.$this->tableName.'
WHERE '.$this->nameColumn.' = ? AND '.$this->passColumn.' = ?';
$result = $this->db->GetAll($sql,array($this->name,md5($this->password)));
$authResult = array(
'code' => Zend_Auth_Result::FAILURE,
'identity' => $this->name,
'messages' => array()
);
$rows = count($result);
if($rows == 0)
{
$authResult['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
$authResult['messages'][] = 'Login failed';
}
else if($rows > 1)
{
$authResult['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS;
$authResult['messages'][] = 'Login failed';
}
else
{
$authResult['code'] = Zend_Auth_Result::SUCCESS;
$authResult['messages'][] = 'Login succesful';
}
return new Zend_Auth_Result($authResult['code'],$authResult['identity'],
$authResult['messages']);
}
Let's look at the code a bit since there's a lot of stuff going on.
First, we generate the SQL query for the authentication, just a simple SELECT statement. We use the PHP md5() function to generate a hash of the password to the select clause; This is because I usually save md5-hashes of users' passwords to the database which is much better security-wise than storing the passwords as plain text.
Then we populate the authResult array which we will use to initialize the Zend_Auth_Result object later. It should be relatively obvious: set default code to failure, identity to the name and messages to an empty array.
After this, we check how many results we got: If we got no rows at all, it means there is no such user so we set the code to FAILURE_IDENTITY_NOT_FOUND and give some generic login failed message. If there's more than one result, it means that for some reason we got more than one user with the same name/pass pair, which should never happen but better be safe than sorry. If we got only one row, it means the authentication was succesful.
Finally, we return a new Zend_Auth_Result object.
Conclusion
As you can see, it is quite easy to extend the Zend_Auth_Adapter to perform authentication from different sources such as ADODB. You could even use file-based databases or anything you like.
There are some things to add though... For example, you may want to make it so that the SELECT clause gets all the fields in the row instead of just the identity column and then have it save the row to a variable and create a get function for it. This way you could then store the row in Zend_Auth's storage. Another thing to consider is how to implement Zend_Acl roles: You could either store the user's role in a column as a varchar or store the roles in a separate table and refer to them with an ID. In the latter case, you would have to change the SELECT so it joins the name of the role from the other table.
Here's the complete source code:
Complete source
<?php
require_once('Zend/Auth/Adapter/Interface.php');
class ADODBAuthInterface implements Zend_Auth_Adapter_Interface
{
private $db;
private $tableName;
private $nameColumn;
private $passColumn;
private $name;
private $password;
public function __construct($db)
{
$this->db = $db;
$this->tableName = 'users';
$this->nameColumn = 'login';
$this->passColumn = 'password';
}
public function setTableName($table)
{
$this->tableName = $table;
return $this;
}
public function setIdentityColumn($nameCol)
{
$this->nameColumn = $nameCol;
return $this;
}
public function setCredentialColumn($passCol)
{
$this->passColumn = $passCol;
return $this;
}
public function setIdentity($name)
{
$this->name = $name
return $this;
}
public function setCredential($pass)
{
$this->password = $pass;
return $this;
}
public function authenticate()
{
$sql = 'SELECT '.$this->nameColumn.'
FROM '.$this->tableName.'
WHERE '.$this->nameColumn.' = ? AND '.$this->passColumn.' = ?';
$result = $this->db->GetAll($sql,array($this->name,md5($this->password)));
$authResult = array(
'code' => Zend_Auth_Result::FAILURE,
'identity' => $this->name,
'messages' => array()
);
$rows = count($result);
if($rows == 0)
{
$authResult['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
$authResult['messages'][] = 'Login failed';
}
else if($rows > 1)
{
$authResult['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS;
$authResult['messages'][] = 'Login failed';
}
else
{
$authResult['code'] = Zend_Auth_Result::SUCCESS;
$authResult['messages'][] = 'Login succesful';
}
return new Zend_Auth_Result($authResult['code'],$authResult['identity'],
$authResult['messages']);
}
}
?>







Anonymous # 3. October 2008, 08:58
Thanks so much
Anonymous # 20. November 2008, 14:01
Thanks for the tutorial. It is just what I've been looking for. Unfortunately, I get the following error:
Warning: Zend_Loader::include_once(ADODBAuthAdapter.php) [zend-loader.include-once]: failed to open stream: No such file or directory in C:\xampplite\htdocs\adodb_dualnback_zf\library\Zend\Loader.php on line 83
Warning: Zend_Loader::include_once() [function.include]: Failed opening 'ADODBAuthAdapter.php' for inclusion (include_path='.;../library/;../application/models;.;\xampplite\php\pear\') in C:\xampplite\htdocs\adodb_dualnback_zf\library\Zend\Loader.php on line 83
Fatal error: Class 'ADODBAuthAdapter' not found in C:\xampplite\htdocs\adodb_dualnback_zf\application\controllers\UserController.php on line 47
It seems to die when it gets here:
$authAdapter = new ADODBAuthAdapter($authdb);
I'm pretty new to ZF and have never used Zend_Auth because our servers don't support PDO_MYSQL. I'm placing ADODBAuthInterface.php in my library directory and am including it in my auth controller like this:
require_once 'ADODBAuthInterface.php';
Any idea what the problem is?
Thanks,
Hugh
zomg # 20. November 2008, 14:08