Lithium and LDAP Authentication
The Lithium framework for PHP 5.3+ is without a doubt my favorite framework (which is evident of course if you've read my blog). I was trying to get LDAP working with Symfony 1.4 and it's sfGuard plugin without a whole lot of luck. While I'm sure possible with more time (perhaps quite a bit), I gave up and just used Lithium because it was much much easier.
The adapters that Lithium uses for various classes are really key. If you wanted to make an adapter for the Auth class, you can really do so without much trouble. Take a look at the current Form adapter as an example. The bulk of what is really required for an adapter is a return from the check() method of false or true or some data. The data returned can then be cached into the user's session data. So basically just returning an array for example will set that data to the session and authenticate the user. Returning false, doesn't. Pretty easy!
Of course there's a lot that might need to go on from A to B but, understanding that, you can make an adapter for just about anything. So I got to cracking on the LDAP authentication. It's a little odd because we aren't logging into a database to then check some records to see if it matches what the user passed. LDAP kinda has all of it's authentication process within itself. You just basically need to pass to it the username and password and it'll return true or false. If it's true you've just bound a connection and can perform searches, etc.
I'm also working on a datasource for LDAP that will really dive into all the CRUD operations. However, for our basic authentication needs all we need is to be able to connect and retrieve the record for the account used to login. Simple.
You can grab the adapter from the Lithium lab. You'll put it under app/extensions/adapters/auth/Ldap.php. Then you'll want to use it pretty much like you'd use the Form adapter. Here's an example configuration:
use \lithium\storage\Session;
use \lithium\security\Auth;
// This is just for an example, you may run with a different session adapter (I keep mine in MongoDB for example)
Session::config(array(
'default' => array('adapter' => 'Php')
));
// Here's what you're after.
Auth::config(array(
'user' => array(
'adapter' => 'Ldap',
'server' => array(
'host' => 'ds1-sjc.entic.net',
'basedn' => 'o=entic.net',
'targetdn' => 'ou=People'
),
'fields' => array('uid'),
'session' => array(
'options' => array('name' => 'default')
)
)
));
What's the entic server? It's an actual LDAP service, a free one (well, paid services too). They let you store some records as one of their services, kinda like a giant Rolodex. I'm not really sure what the status of their project is, but I do know it was one way to test the LDAP for free. They actually have a few interesting services hosted "in the cloud" (and they are working on some more) so you may want to give them a look.
So what is going on here? Well, we have our host then we have the "basedn" which is typically o=something. It could also commonly be something like: dc=example,dc=com. Anyway...Then we have a "targetdn" which is basically just some more properties to tack on. I've left this key separate purely for organization reasons and who knows, you may need to alter the property for some reason independent of the basedn value. It could have been defined under the basedn. Your distinguished name requirements are going to vary based on LDAP server. Then you have the fields key which is yet again more properties to append to this long DN string that's required to login. In the case of this server it's "uid" and again this list of fields (which also exist in your login form) are pieced together to form the DN. Since we aren't going to present the user with a form to enter this long DN in...We just have the "uid" field exposed in our form. We could have shown some other fields maybe as a drop down option for a group. Again, this depends on your LDAP server's configuration. Now we also need to pass from the form a "password" field. It won't be appended to the DN, but it is of course passed along to login to the LDAP server. Here's what our login view template looks like:
<?=$this->form->create(); ?>
<?=$this->form->field('uid', array('label' => 'Username')); ?>
<?=$this->form->field('password', array('type' => 'password')); ?>
<?=$this->form->submit('Log in'); ?>
<?=$this->form->end(); ?>
I'm not going to go over in great detail how to setup Auth. I'm assuming you've used the Form adapter before. However, if you haven't ever setup auth before, you'll want to put that first Auth::config() part somewhere in your bootstrap process. You'll also need to protect methods in one of many manners. You might apply a filter to the Dispacther's run() method for example in your bootstrap right after the Auth::config() part. You're going to be calling Auth::check() to assist with whatever you do to protect parts of your app. So let's say you also have a controller (let's say UsersController which is quite common) that has a login() that looks something like this:
public function login() {
if($this->request->data) {
$user = Auth::check('user', $this->request);
}
if($user) {
// Might want to redirect somewhere else
$this->redirect('/pages/login_successful');
}
$data = $this->request->data;
return compact('data');
}
That's all there is to it! So now when you to go /users/login and use the form to login. What will then happen is upon a successful login (bind) to the LDAP server, the account record will be retrieved and set to the session data. So when you call Auth::check('user'); you will be returned with an array of data. You can use this to do whatever you need.
Pretty easy, right? This is strictly authentication, you aren't writing anything back to the LDAP server. That's what you'll need a datasource for and I promise to share that as well. It's currently under construction. Again, you can grab the LDAP Auth adapter here.


[Back To Blog Index]