Matthew Bull

web development

symfony’s sfGuard plugin and LDAP

June 29, 2008 – 13:54 GMT - Matthew Bull

A while ago I needed to hook up symfony’s excellent sfGuard plugin to some LDAP functionality. There are a couple of issues with the plugin and the readme which I think need fixing. In particular, there is no support for checking both LDAP and standard sfGuard passwords. This is absolutely essential (eg an admin user or guest users who aren’t in LDAP), and I’m somewhat amazed that there’s no provision for this. Moreover, the plugin structure generally makes it seemingly impossible (or if it is possible it’s just too horrible to contemplate) to write your own checkPassword() which does do both sorts of checking.

Anyway this post is partly an attempt for me to get my head round something which I’ve either totally misunderstood, or just isn’t well documented.

 

Confusing README

The README file that comes with the plugin is great, but it’s a little confusing for explaining how you might override its own way of checking passwords with something that’s LDAP-based. It’s really very simple to do when written out in plain English:

1. Make a file called myLDAP.class.php in your application’s lib folder. Put this in it:

class myLDAP extends sfGuardSecurityUser
{
}

2. Now put a method inside the class you just created:
public static function checkLDAPPassword($username, $password)
{
// put your preferred LDAP-checking code in here
// should return true or false
}

3. Add this to your application’s app.yml file:

all:
  sf_guard_plugin:
    check_password_callable: [myLDAP, checkLDAPPassword]

where myLDAP is the file called myLDAP.class.php that you created in step 1 above. checkLDAPPassword is the method you made inside myLDAP.class.php that you made in step 2.

And there you have it. Basic LDAP password checking.

 

How does checkPassword() actually work?

The above code is all very well, but what’s really going on?

Well, there are a couple of steps which I managed to work out:

1. sfGuardUserValidator contains a call to $user->checkPassword($password) where $user is a result of sfGuardUserPeer::retrieveByUsername($username)

2. PluginsfGuardUser (extends BasesfGuardUser) has a method called checkPassword() too. This calls the method you listed in your app.yml file (checkLDAPPassword in this case), or does a standard sfGuard password check (checkPasswordByGuard) if you didn’t specify anything:

public function checkPassword($password)
{
if ($callable = sfConfig::get('app_sf_guard_plugin_check_password_callable'))
{
return call_user_func_array($callable, array($this->getUsername(), $password));
}
else
{
return $this->checkPasswordByGuard($password);
}
}

Which it does depends on how you set up your app.yml file (see Step 3 above).

At this point, if you set up your app.yml correctly you’ll be in your custom checkLDAPPassword() method, which will do whatever you want it to do to check LDAP passwords. So long as it returns true or false depending on whether the check was successful or not, you’ll be ok.

Note that sfGuardSecurityUser class also contains a checkPassword() method. This basically does the same thing as the in sfGuardUserValidator, but in a slightly different way, and allows access to checkPassword() in templates and controllers through the $sf_user object. The important thing to realise is that both pieces of code end up calling the same checkPassword() method in PluginsfGuardUser.

 

So how about LDAP and sfGuard checking?

Now the fun begins. The problem is that it’s seemingly impossible to call checkPasswordByGuard() yourself from inside your custom-built checkLDAPPassword() method. Why would you want to do this? Because that way you can do some standard password checking first to see if your user is in fact a sfGuard user and not an LDAP user. Only if that failed would you do LDAP checking (I suppose you could do it the other way round if you wanted.) Even better, you wouldn’t need to alter checkPasswordByGuard().

So why is it impossible to call checkPasswordByGuard() ‘by hand’? As far as I can see, it assumes that you’re not in a static context, and that you have some kind of user object available. But of course, my checkLDAPPassword() is in a static context. It had to be that way because it was called by call_user_func_array() which uses the output of sfConfig::get() as an argument.

Sigh…

If you find a way of doing this, please let me know. Losing the will to live is merely one of the symptoms of battling with this sort of thing. I have a life to live.

All I can suggest to you in the meantime is to modify your copy of PluginsfGuardUser.php to allow both sorts of password checking. That’s what I did and it worked for me. It’s just a pity that you can’t override the checkPasswordByGuard() method in some way without changing the core of the plugin.

  1. 9 Responses to “symfony’s sfGuard plugin and LDAP”

  2. Hello,

    I’m very interested in this article, trying to create a authentication using sfGuard and LDAP.
    There is not a lot of documentation on this, so i appreciate a lot your article.
    Though your 3 steps does not work for me, when i do what you say, i still have a :

    Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, ‘MyLDAP::checkLDAPPassword’ was given in /mysymphonyproject/plugins/sfGuardPlugin/lib/model/plugin/PluginsfGuardUser.php on line 62

    Apparently it never goes to my myLDAPP.class.php located in the folder : mysymphonyproject/apps/frontend/lib

    Do you know where it could come from ?
    I can’t find any solution and i can’t see the end of it :s

    By Julien on Jul 4, 2008

  3. Julien,
    I think your problem is probably just a typo. You need to make sure the class and method names are exactly the same (including upper and lower cases) in your app.yml as in the class file. At least, when I tried changing the class name in my app.yml to MyLDAP rather than myLDAP I got exactly the same error message as you.

    Anyway hope you sort things out because I know how frustrating it can be when you’re so close to getting it all to work!

    By admin on Jul 5, 2008

  4. Hello,

    You were right.
    Thank you again !

    By Julien on Jul 7, 2008

  5. Hello again,
    Sorry to be a little a pain in the ass, but i do have another question on the same area.
    It appears that my program authentificates via LDAP only when i type “admin” as a username. if i don’t then it will just reject usnme & psswd, and won’t use my class anyway.
    Do you have any idea ?

    By Julien on Jul 7, 2008

  6. I’m not sure what your problem is exactly. If you’ve set up the plugin according to the sfGuard instructions, including the right app.yml setup, then it should work. If it’s verifying the admin user and not LDAP users then I can only guess there’s a problem with the app.yml file and so it’s just never using your LDAP class. Sorry I can’t help more without seeing the app.yml file.

    By matthew on Jul 14, 2008

  7. Did you try the following?
    $user = sfGuardUserPeer::retrieveByUsername($username);
    return $user->checkPasswordByGuard($password);

    the new version (for Symfony 1.1) contains the $user object as a third parameter in the callable I believe, so you don’t have to call the retrieveByUsername thus saving one SQL statement.

    By Ben on Aug 3, 2008

  8. Ah great thanks! I haven’t had time to look in detail at 1.1 yet but I’ll give that a try.

    By matthew on Aug 4, 2008

  1. 2 Trackback(s)

  2. Jul 1, 2008: rpsblog.com » A week of symfony #78 (23->29 june 2008)
  3. Aug 18, 2008: The Codebelay Blog » Installing sfGuardPlugin in symfony 1.1 — A Guide for the Perplexed

Post a Comment