Jump to content

LDAP Auth for Session login


Jim Yost
 Share

Recommended Posts

Howdy!

Just created a module for authenticating via LDAP. It works great for our implementation of LDAP, however I'm sure it may need additional work.

Looking for some feedback if there are better ways to implement this or clean up my code a bit.

File attached.

-Jim

  • Like 1
Link to comment
Share on other sites

<?php
class LdapAuth extends WireData implements Module, ConfigurableModule {
public static function getModuleInfo() {
 return array(
  "title" => "LDAP Authentication",
  "version" => 100,
  "summary" => "Allows uses to be authenticated via LDAP",
  "permanent" => false,
  "singular" => true,
  "autoload" => true
 );
}

public function init() {
 $this->session->addHookAfter('login', $this, 'login');
}

public function ___login($event) {
 if ($event->return) return; // they are already in

 $name = $event->arguments[0];
 $pass = $event->arguments[1];

 $conn = ldap_connect($this->data['host']);
 if ($conn) {
  $bind = @ldap_bind($conn, "$name@{$this->data['accountDomainName']}", $pass);

  if ($bind) {
   // success
   // check if they area lready a user in PW
   $user = wire('users')->get("name=$name");
   if (!$user instanceof NullPage) {
 // update login info
 $user->pass = $pass;
 $user->save();

 $user = wire('session')->login($name, $pass);
 $event->return = $user;
 return;
   } else {
 // create a new user
 $guest = wire('users')->getGuestUser();
 $user = new User();
 $user->parent = $guest->parent;
 $user->name = $name;
 $user->pass = $pass;
 $user->addRole("guest");
 $user->save();

 $user = wire('session')->login($name, $pass);
 $event->return = $user;
 return;
   }
  } else {
   // fail
   $event->return = null;
   return;
  }
 } else {
  // could not connect
  throw new Exception("Could not connect to LDAP");
 }
}

static public function getModuleConfigInputfields(array $data) {
 $inputfields = new InputfieldWrapper();

 $field = Wire::getFuel('modules')->get('InputfieldText');
 $field->attr('name', 'host');
 $field->label = 'Host';
 if (isset($data['host'])) $field->attr('value', $data['host']);
 $field->description = 'The LDAP server hostname';
 $inputfields->append($field);

 $field = Wire::getFuel('modules')->get('InputfieldText');
 $field->attr('name', 'accountDomainName');
 $field->label = 'Account Domain Name';
 if (isset($data['accountDomainName'])) $field->attr('value', $data['accountDomainName']);
 $field->description = 'The LDAP server domain';
 $inputfields->append($field);

 $field = Wire::getFuel('modules')->get('InputfieldText');
 $field->attr('name', 'accountDomainNameShort');
 $field->label = 'LDAP server domain (short)';
 if (isset($data['accountDomainNameShort'])) $field->attr('value', $data['accountDomainNameShort']);
 $field->description = 'The LDAP server hostname';
 $inputfields->append($field);

 $field = Wire::getFuel('modules')->get('InputfieldText');
 $field->attr('name', 'baseDn');
 $field->label = 'Base DN';
 if (isset($data['baseDn'])) $field->attr('value', $data['baseDn']);
 $field->description = 'The LDAP server DN';
 $inputfields->append($field);

 $field = Wire::getFuel('modules')->get('InputfieldCheckbox');
 $field->attr('name', 'startTls');
 $field->label = 'Use TLS';
 $field->attr('value', 1);
 if (isset($data['startTls'])) {
  if ($data['startTls']) $field->attr('checked', true);
 }
 $field->description = 'Check this option to enable TLS security';
 $inputfields->append($field);
 return $inputfields;
}
}
  • Like 2
Link to comment
Share on other sites

Great stuff! I know that in our company we will need this for sure, so big thanks from here.

I have never implemented ldap myself, so cannot comment much. Only suggestion is to rename your class to SessionLdapAuth so it would go into session category on modules list.

Link to comment
Share on other sites

public function ___login($event) {

لهذا

public function login($event)

لا يوجد سبب ليكون آخر هوك هنا

تغيير

if (!$user instanceof NullPage) {
  // update login info
  $user->pass = $pass;
  $user->save();

لهذا

if($user->id) {
 $user->pass = $pass;
 if($user->isChanged('pass')) $user->save();

تعديل هذا

$guest = wire('users')->getGuestUser();
$user = new User();
$user->parent = $guest->parent;
$user->name = $name;
$user->pass = $pass;
$user->addRole("guest");
$user->save();
$user = wire('session')->login($name, $pass);

لهذا

$user = wire('users')->add($name);

$user->pass = $pass;
$user->addRole('guest');
$user->save();
wire('users')->setCurrentUser($user);

تعديل هذا

throw new Exception("Could not connect to LDAP");

لهذا

throw new WireException($this->_('Could not connect to LDAP'));

يودا من حرب النجوم

$field = Wire::getFuel('modules')->get('InputfieldText');

لهذا

$field = wire('modules')->get('InputfieldText'); 
Link to comment
Share on other sites

I don't know what WillyC was saying above, but his code suggestions are right on. I agree with everything he said (in the code at least). Also, Jim this is one awesome module you've put together. Nice work!

Edit: WillyC please use English, if possible. People are reporting your post as spam (and I know it's not, your post was good). Admittedly, I never understand your drunken-refugee-Yoda English, but I think even fewer here understand your Klingon (?) or whatever that is. I do admit it looks very pretty though (as does your beard). But please use English where possible, or at least make us think you are... Ultimately if we can understand your code, that's all that's necessary, and you are doing well there.

Link to comment
Share on other sites

...Klingon...

:D It's arabic, I believe... and particulary for this: 'يودا من حرب النجوم', translate.google.com said it means 'Yoda from Star Wars'. Other than that, I give +1 to using english. We are international here and we won't understand each other, if we start talking our native languages (except Ryan and few others, there would nothing change. :D)

  • Like 1
Link to comment
Share on other sites

Nice, Jim, even if I have no idea of ldap auth.

As I'm working on a module that serves as a kind of manager for different auth modules () and will provide standard login, Twitter and Facebook auth, it would be great to have a ldap auth module for it, too. When you're ok with it, I'd build one based on your code here, as it mostly just needs a bit of reordering.

Link to comment
Share on other sites

Nice module Jim - this is very handy for one project I've got coming up as I can now integrate it with the company's domain to some degree and not have the hassle of people trying to change/remember their passwords in multiple places.

It's also going to be a good starting point for another sooner project that was going to require me to code something very similar, so thanks as that will be coded in no time at all working from your module! :)

In arabic, shouldn't the sentences be on the right?

I would have to change a forum setting and that would affect everyone's post, but it's possible to have everything appear right-to-left if you like ;)

Link to comment
Share on other sites

Thanks WillyC, that is exactly what I was looking for!

The module still needs work with ldap settings (specifically TLS and other connection types that LDAP works with). I just needed it to show my company how flexible ProcessWire is :-).

Oliver, let me get a formal module together that supports more complete features of LDAP before you grab it (though you are welcome to do so). I started one for looking up users, groups, and pulling information as well and will end up tieing the two together.

-J

Link to comment
Share on other sites

  • 5 months later...

I tested this in one project and it works great! I implemented the WillyC improvements and added the Session prefix to the name. Code is here:

<?php
class SessionLdapAuth extends WireData implements Module, ConfigurableModule {
public static function getModuleInfo() {
 return array(
  "title" => "LDAP Authentication",
  "version" => 100,
  "summary" => "Allows uses to be authenticated via LDAP",
  "permanent" => false,
  "singular" => true,
  "autoload" => true
 );
}
public function init() {
 $this->session->addHookAfter('login', $this, 'login');
}
public function login($event) {
 if ($event->return) return; // they are already in
 $name = $event->arguments[0];
 $pass = $event->arguments[1];
 $conn = ldap_connect($this->data['host']);
 if ($conn) {
  $bind = @ldap_bind($conn, "$name@{$this->data['accountDomainName']}", $pass);
  if ($bind) {
    // success
    // check if they area lready a user in PW
    $user = wire('users')->get("name=$name");
    if($user->id) {
	  $user->pass = $pass;
	  if($user->isChanged('pass')) $user->save();
	 $user = wire('session')->login($name, $pass);
	 $event->return = $user;
	 return;
    } else {
	 // create a new user
	  $user = wire('users')->add($name);
	  $user->pass = $pass;
	  $user->addRole('guest');
	  $user->save();
	  wire('users')->setCurrentUser($user);
	 $event->return = $user;
	 return;
    }
  } else {
    // fail
    $event->return = null;
    return;
  }
 } else {
  // could not connect
  throw new WireException($this->_('Could not connect to LDAP'));
 }
}
static public function getModuleConfigInputfields(array $data) {
 $inputfields = new InputfieldWrapper();
 $field = wire('modules')->get('InputfieldText');
 $field->attr('name', 'host');
 $field->label = 'Host';
 if (isset($data['host'])) $field->attr('value', $data['host']);
 $field->description = 'The LDAP server hostname';
 $inputfields->append($field);
 $field = wire('modules')->get('InputfieldText');
 $field->attr('name', 'accountDomainName');
 $field->label = 'Account Domain Name';
 if (isset($data['accountDomainName'])) $field->attr('value', $data['accountDomainName']);
 $field->description = 'The LDAP server domain';
 $inputfields->append($field);
 $field = wire('modules')->get('InputfieldText');
 $field->attr('name', 'accountDomainNameShort');
 $field->label = 'LDAP server domain (short)';
 if (isset($data['accountDomainNameShort'])) $field->attr('value', $data['accountDomainNameShort']);
 $field->description = 'The LDAP server hostname';
 $inputfields->append($field);
 $field = wire('modules')->get('InputfieldText');
 $field->attr('name', 'baseDn');
 $field->label = 'Base DN';
 if (isset($data['baseDn'])) $field->attr('value', $data['baseDn']);
 $field->description = 'The LDAP server DN';
 $inputfields->append($field);
 $field = wire('modules')->get('InputfieldCheckbox');
 $field->attr('name', 'startTls');
 $field->label = 'Use TLS';
 $field->attr('value', 1);
 if (isset($data['startTls'])) {
  if ($data['startTls']) $field->attr('checked', true);
 }
 $field->description = 'Check this option to enable TLS security';
 $inputfields->append($field);
 return $inputfields;
}
}
  • Like 6
Link to comment
Share on other sites

Very nice, thanks for posting this! What do you think about putting this on GitHub so that it can be installed through the modules directory? I would do it myself, but have no way of testing LDAP, so don't think my GitHub account would be a good one to have it on. I also know that the original author (JimYost--my brother-in-law) is really busy at work and not likely to post or develop this further. But it seems like a pretty unique and great module, so would be great to have it as something more than a copy/paste, if/when you or someone else using it has time.

Link to comment
Share on other sites

  • 2 months later...

I just used this to add AD login abilities to a company docs intranet site for PW and would like to suggest some changes.

In this particular environment, the AD logins are in the format of "Forename Surname" <- the problem here is the space in the middle and PW replaces it with an underscore before the check against the LDAP server so it fails and doesn't authenticate properly. The following fixes it:

Replace this:

$name = $event->arguments[0];
$pass = $event->arguments[1];

with this:

$name = wire('input')->post->login_name;
$pass = wire('input')->post->login_pass;

And this:

$user = wire('users')->get("name=$name"

with this:

$user = wire('users')->get("name=".$event->arguments[0]);

Any other suggestions welcome, but that should work correctly in more situations.

Antti, did you do any more work with AD groups mapped to roles?

  • Like 3
Link to comment
Share on other sites

No worries - just wanted to check I wasn't going to reinvent the wheel before going any further.

All I really need that's extra is to pull out the user's name and email address for now - groups is a bit beyond me. I know that using adLDAP I can get more information but I'd rather not use another class just for that.

I'll have a tinker and see what I can come up with, but so far this solves a huge problem of not wanting to give users in a Windows domain environment yet another login to remember :)

Link to comment
Share on other sites

  • 9 months later...
  • 2 years later...
  • 2 months later...

Hallo,

how can i change the Module to login every User with LDAP? The Problem is when a User change his LDAP Password, he can login on Processwire with the old password so long he not login with the new Password! That is a big Security Problem. So i will authenticate every User by checking the LDAP. Any Idea?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...