Jump to content
Jim Yost

LDAP Auth for Session login

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

Share this post


Link to post
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 1

Share this post


Link to post
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.

Share this post


Link to post
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'); 

Share this post


Link to post
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.

Share this post


Link to post
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

Share this post


Link to post
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.

Share this post


Link to post
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 ;)

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

Thanks Jim. I just started the module, so there’s no hurry. Gives me time to get an idea of LDAP. ;)

Share this post


Link to post
Share on other sites

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 5

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

Sure. I'll put it to github soon. We have few ideas to develop it further. Things like bringing more user details like email, but most importantly map ad groups to roles.

  • Like 3

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Nope, sorry. My need in that project was just the authentication.

Share this post


Link to post
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 :)

Share this post


Link to post
Share on other sites

Please, could someone tell me if I am right or wrong? I'm into a new project at my current job, I've to find a plugin or module for LDAP to keep the session connection. Does LDAP support the sessions? Sorry but I am very new into LDAP. Any suggestion is welcome.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...