LDAP Plugin for CC 2.5

edited March 2017 in Customizations
Hey guys, I am not a PRO when it comes to PHP development but I think I've managed to further enhance the LDAP plugin and make it functional with CC 2.5.

Feel free to report feedback, good or bad.

Cheers


<?php

class LDAP extends PluginAbstract
{
public $name = 'LDAP Plugin';
public $description = 'LDAP Authentication';
public $author = 'various';
public $url = 'http://www.cumulusclips.org';
public $version = '1.0.1';

public function load()
{
Plugin::attachEvent('login.end', array($this, 'LDAPCode'));
}

private function handleException($view, $errorMessage)
{
$view->vars->message = $errorMessage;
$view->vars->message_type = "errors";
}

public function LDAPCode($view)
{
if ((isset($_POST['submitted_login'])) && (!(empty($view->vars->username))) && (!(empty($view->vars->password))))
{
$username = $view->vars->username;
$password = $view->vars->password;

$userMapper = new UserMapper();
$userService = new UserService();
$authService = new AuthService();

// To trust selfsigned SSL certs add "TLS_REQCERT never" to /etc/openldap/ldap.conf
$ldapurl = "ldaps://server.mydomain.org:636";
$basedn = "DC=mydomain,DC=org";
$filter = "(sAMAccountName=[username])";
$userdn = $filter;

$ldaprdn = $username . "@mydomain.org";
$ldappass = $password;

if ($authService->validateCredentials($username, $password))
{
# We're authenticated
}
elseif ($ldapconn = ldap_connect($ldapurl))
{
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);

// Enable tracing...
// ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

if ($ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass))
{
$search = str_replace("[username]", $username, $userdn);

if (!preg_match('/\((.)*=(.)*\)/',$search))
{
throw new Exception("Failed: search has been used but '$search' is NOT a filter.");
}

if ($result = ldap_search($ldapconn,$basedn,$search))
{
$entries = ldap_get_entries($ldapconn, $result);
$binddn = $entries[0]["dn"];

if ($ldapbind = ldap_bind($ldapconn, $binddn, $view->vars->password))
{
if ($entries)
{
$_SESSION['user'] = $username;
$_SESSION['loggedInUserId'] = $username;
$_SESSION['name'] = $entries[0]["cn"][0];
$_SESSION['email'] = $entries[0]['mail'][0];
$_SESSION['firstname'] = $entries[0]['givenname'][0];
$_SESSION['lastname'] = $entries[0]['sn'][0];
}
ldap_close($ldapconn);

// Creation of local user account if one does not exist
if (!$userMapper->getUserByUsername($username))
{
//Code to create the new user
$user = new User();
$user->username = $username;
$user->password = md5(SECRET_KEY.'PASSWORD');
$user->email = $_SESSION['email'];
$user->firstName = $_SESSION['firstname'];
$user->lastName = $_SESSION['lastname'];
$newUser = $userService->create($user);

//Local user activation
$newUser->status = 'active';
$newUser->lastLogin = date(DATE_FORMAT);
$userMapper->save($newUser);

// Send Admin Alert
if (Settings::Get ('alerts_users') == '1')
{
$subject = 'New Member Registered';
$body = 'A new member has registered.';
$body .= "\n\n=======================================================\n";
$body .= "Username: $user->username\n";
$body .= "Profile URL: " . HOST . "/members/$user->username/\n";
$body .= "=======================================================";
App::Alert ($subject, $body);
}

// $config = Registry::get('config');
// $mailer = new Mailer($config);
// $mailer->setTemplate ('account_approved', array('sitename' => $config->sitename));
// $mailer->Send ($user->email);
}

$user = $userMapper->getUserByCustom(array(
'username' => $username,
'status' => 'active'
));

if ($user)
{
$authService->login($user);
$user->lastLogin = date(DATE_FORMAT);
$userMapper->save($user);
$url = ($view->vars->redirect) ? $view->vars->redirect : HOST . '/account/';
$url = Plugin::triggerFilter('login_redirect', $url);
header('Location: ' . $url);
}
else
{
LDAP::handleException($view, "ERROR: Login Failed (1002)");
}
}
else
{
LDAP::handleException($view, "ERROR: Login Failed (1003)");
}
}
else
{
ldap_close($ldapconn);
LDAP::handleException($view, "ERROR: Could not log you in, please try again.");
}
}
else
{
ldap_close($ldapconn);
LDAP::handleException($view, "ERROR: Inccorect username or password");
}
}
else
{
LDAP::handleException($view, "ERROR: Incorrect LDAP Settings (1004)");
}
}
}
}
?>



Comments

  • This is a great start. May I recommend adding a settings page for your plugin so that users can specify their domain; along with any other user specific value. This way the values aren't hard coded and the user can change them via the Admin Panel.

    Our documentation is lacking when it comes to settings pages for plugins. Follow along the plugin_settings.php file in the admin panel, the PluginAbstract.php, and Plugin.php classes in cc-core/lib. If you have any questions I'm always here to help.
  • Very good plugin. I have problem with some users who have a '-' like "j-doe". But it's normal because the CC username must contain alphanumeric (a-z, 0-9) characters, no spaces or special characters.

    The user is created, but when I go to : http://monserveur/members/j-doe/ I have a 404 not found.

    Thanks.
  • @o71 Please create a seperate post for your issue.
  • Sorry. It's not really a problem, just an information.
  • Thank you for expanding on the old LDAP plugin. It has come a long way since I first started looking into it. Have you had any luck with a plugin that works with php 7?
  • @GreenMotion : Thanks a lot for this plugin, it worked like a charm. But still ldap login does not work for mobile site. Do we need any additional change for this.
  • @deepakchourasia: You are right, it does not work for the mobile site. I posted a message about that to Damian last year and he confirmed that the mobile site doesn't use the same authentication abstraction layer. I was going to look into this some more, but I believe the mobile theme is being deprecated and integrated into a regular responsive type of theme ... if I remember right?

    @Damian?
  • @GreenMotion : Thanks a lot for the response!!, but if there is more time before we can get the mobile theme merged with the regular one, it will be really great if you could look into the code and provide some hints on how we can implement ldap with current mobile theme.

    @Damian : could you please confirm when the mobile theme is being deprecated?
  • I was able to enable LDAP for mobile site by manipulating the plugin code to interact with cc-core\controllers\ajax\login.php. Let me know if somebody needs it.
  • @deepakchourasia; if you could share your solution, that would be great!
  • @GreenMotion : here is the modified code for cc-core\controllers\ajax\login.php, this will enable LDAP for mobile site as well, once LDAP settings are updated in this file.

    Though I could have managed to merge this into a single plugin for both the sites, but because of time constraint I went ahead with this quick fix.



    <?php


    $loggedInUser = $this->authService->getAuthUser();
    Functions::redirectIf(!$loggedInUser, HOST . '/account/');


    $userService = new UserService();
    $this->view->options->disableView = true;
    $username = null;
    $password = null;


    $this->view->vars->login_submit = true;


    if (!empty($_POST['username'])) {
    $username = trim($_POST['username']);
    }


    if (!empty($_POST['password'])) {
    $password = trim($_POST['password']);
    }


    if ($username && $password) {

    if ($user = $this->authService->validateCredentials($username, $password)) {
    $this->authService->login($user);
    exit(json_encode(array(
    'result' => true,
    'message' => null,
    'other' => null
    )));
    } else if(LDAPCode($username, $password, $this->authService))
    {
    exit(json_encode(array(
    'result' => true,
    'message' => null,
    'other' => null
    )));
    }
    else
    {
    exit(json_encode(array(
    'result' => false,
    'message' => Language::getText('error_invalid_login'),
    'other' => null
    )));
    }

    } else {
    exit(json_encode(array(
    'result' => false,
    'message' => Language::getText('error_general'),
    'other' => null
    )));
    }

    function LDAPCode($username, $password, $authService)
    {

    $loginSuccessful = false;

    if (!empty($username) && !empty($password))
    {

    $userMapper = new UserMapper();
    $userService = new UserService();

    $ldapurl = "ldaps://server.mydomain.org:636";
    $basedn = "DC=mydomain,DC=org";
    $filter = "(sAMAccountName=[username])";
    $userdn = $filter;

    $ldaprdn = $username . "@mydomain.org";
    $ldappass = $password;

    if ($authService->validateCredentials($username, $password))
    {
    # We're authenticated
    }
    elseif ($ldapconn = ldap_connect($ldapurl))
    {
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);

    // Enable tracing...
    // ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

    if ($ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass))
    {

    $search = str_replace("[username]", $username, $userdn);

    if (!preg_match('/\((.)*=(.)*\)/',$search))
    {
    throw new Exception("Failed: search has been used but '$search' is NOT a filter.");
    }

    if ($result = ldap_search($ldapconn,$basedn,$search))
    {
    $loginSuccessful = true;

    $entries = ldap_get_entries($ldapconn, $result);
    $binddn = $entries[0]["dn"];

    if ($ldapbind = ldap_bind($ldapconn, $binddn, $password))
    {
    if ($entries)
    {
    $_SESSION['user'] = $username;
    $_SESSION['loggedInUserId'] = $username;
    $_SESSION['name'] = $entries[0]["cn"][0];
    $_SESSION['email'] = $entries[0]['mail'][0];
    $_SESSION['firstname'] = $entries[0]['givenname'][0];
    $_SESSION['lastname'] = $entries[0]['sn'][0];
    }
    ldap_close($ldapconn);

    // Creation of local user account if one does not exist
    if (!$userMapper->getUserByUsername($username))
    {
    //Code to create the new user
    $user = new User();
    $user->username = $username;
    $user->password = md5(SECRET_KEY.'PASSWORD');
    $user->email = $_SESSION['email'];
    $user->firstName = $_SESSION['firstname'];
    $user->lastName = $_SESSION['lastname'];
    $newUser = $userService->create($user);

    //Local user activation
    $newUser->status = 'active';
    $newUser->lastLogin = date(DATE_FORMAT);
    $userMapper->save($newUser);

    // Send Admin Alert
    if (Settings::Get ('alerts_users') == '1')
    {
    $subject = 'New Member Registered';
    $body = 'A new member has registered.';
    $body .= "\n\n=======================================================\n";
    $body .= "Username: $user->username\n";
    $body .= "Profile URL: " . HOST . "/members/$user->username/\n";
    $body .= "=======================================================";
    App::Alert ($subject, $body);
    }
    }

    $user = $userMapper->getUserByCustom(array(
    'username' => $username,
    'status' => 'active'
    ));

    if ($user)
    {
    $authService->login($user);
    $user->lastLogin = date(DATE_FORMAT);
    $userMapper->save($user);
    }
    else
    {
    handleException("ERROR: Login Failed (1002)");
    }
    }
    else
    {
    handleException("ERROR: Login Failed (1003)");
    }
    }
    else
    {
    ldap_close($ldapconn);
    handleException("ERROR: Could not log you in, please try again.");
    }
    }
    else
    {
    ldap_close($ldapconn);
    handleException("ERROR: Incorrect username or password");
    }
    }
    else
    {
    handleException("ERROR: Incorrect LDAP Settings (1004)");
    }
    }
    return $loginSuccessful;
    }

    function handleException($errorMessage)
    {
    exit(json_encode(array(
    'result' => false,
    'message' => $errorMessage,
    'other' => null
    )));
    }

  • We've actually decided to include this (LDAP connectivity) into the next major release of CumulusClips. @GreenMotion, how are testing your code, do you have a working LDAP server?
  • Hey @Damian; sorry for the late reply. My instance does talks to a "Active Directory" server via LDAP, but its only accessible from the internal network :( I would however be more than happy to test a pre-release version if you like. You can email me at my gmail account. (just add @gmail.com to my handle)
Sign In or Register to comment.