Skip to content

Instantly share code, notes, and snippets.

@thewatts
Forked from anonymous/gist:b61376e81144608f2fe0
Created December 10, 2014 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thewatts/b4e95b819d652b8d0df4 to your computer and use it in GitHub Desktop.
Save thewatts/b4e95b819d652b8d0df4 to your computer and use it in GitHub Desktop.
<?php if ( ! defined('EXT') ) exit('Invalid file request');
/** FINAL
* LDAP Authentication
*
* ### EE 2.1 version ###
*
* Based on: NCE LDAP
* http://code.google.com/p/ee-ldap-extension/
* License: "if you've used this module and found that it needed something then please hand it back so that it can be shared with the world"
* Site: http://code.google.com/p/ee-ldap-extension/wiki/Introduction
*
* An ExpressionEngine Extension that allows the authentication of users via LDAP
* LDAP details are copied to the EE database before standard MySQL authentication is performed
* If user is not found on LDAP, MySQL authentication will still be performed (useful for EE users not in LDAP)
*
* Dependancy: iconv PHP module
*
* @package DesignByFront
* @author Alistair Brown
* @author Alex Glover
* @link http://github.com/designbyfront/LDAP-Authentication-for-ExpressionEngine
* @since Version 1.3
*
* LDAPS Instructions - http://github.com/designbyfront/LDAP-Authentication-for-ExpressionEngine/issues/closed#issue/1
*
* Enhancements to original:
* - Upgraded to EE2
* - Authentication against multiple LDAP servers
* - Non-LDAP user login (remove restriction)
* - Authentication even with LDAP server downtime (remove restriction)
* - Use EE global classes (rather then PHP global variables)
* - DB protection against injection (however unlikely)
* - Better code structure using functions
* - More settings control:
* - Use of character encoding for sent data (and ability to change in settings)
* PHP uses 'UTF-8' encoding; Windows server uses 'Windows-1252' encoding.
* Using the iconv PHP module, settings data saved in 'UTF-8' is dynamically encoded to 'Windows-1252' when being sent.
*
*/
class Nce_ldap_ext {
/*
// PHP4 Constructor
function Nce_ldap_ext($settings = '')
{
$this->EE =& get_instance();
$this->settings = $settings;
}
*/
var $name = 'UNT LDAP auth (front ldap mod)';
var $version = '1.4';
var $description = 'Handles LDAP login / account creation. Modified by Blair';
var $settings_exist = 'y';
var $docs_url = 'http://github.com/designbyfront/LDAP-Authentication-for-ExpressionEngine/issues';
var $settings = array();
var $use_ldap_account_creation = 'yes';
var $ldap_character_encode = 'Windows-1252';
var $groupID_facultystaff = 9;
var $groupID_student = 8;
var $groupID_alumni = 4;
var $debug = TRUE;
function __construct()
{
$this->EE =& get_instance();
$this->settings = $settings;
}
// ----------------------
/**
* EE method called when the extension is activated
*/
function activate_extension ()
{
$settings['use_ldap_account_creation'] = $this->use_ldap_account_creation;
$settings['ldap_character_encode'] = $this->ldap_character_encode;
$settings['groupID_facultystaff'] = $this->groupID_facultystaff;
$settings['groupID_student'] = $this->groupID_student;
$settings['groupID_alumni'] = $this->groupID_alumni;
$hooks = array(
'login_authenticate_start' => 'login_authenticate_start',
'member_member_login_start' => 'member_member_login_start'
);
foreach ($hooks as $hook => $method)
{
$this->EE->db->query($this->EE->db->insert_string('exp_extensions',
array(
'extension_id' => '',
'class' => __CLASS__,
'method' => $method,
'hook' => $hook,
'settings' => serialize($settings),
'priority' => 10,
'version' => $this->version,
'enabled' => "y"
)
));
}
}
// ----------------------
/**
* EE method called when the extension is updated
*/
function update_extension($current = '')
{
if ($current == '' OR $current == $this->version)
return FALSE;
$this->EE->db->query('UPDATE exp_extensions SET version = \''.$this->EE->db->escape_str($this->version).'\' WHERE class = \''.$this->EE->db->escape_str(__CLASS__).'\'');
}
// ----------------------
/**
* EE method called when the extension is disabled
*/
function disable_extension()
{
$this->EE->db->query('DELETE FROM exp_extensions WHERE class = \''.$this->EE->db->escape_str(__CLASS__).'\'');
}
// ----------------------
/**
* Configuration for the extension settings page
*/
function settings()
{
$settings['ldap_character_encode'] = $this->ldap_character_encode;
$settings['groupID_facultystaff'] = $this->groupID_facultystaff;
$settings['groupID_student'] = $this->groupID_student;
$settings['groupID_alumni'] = $this->groupID_alumni;
$settings['use_ldap_account_creation'] = array('r', array('yes' => 'yes_ldap_account_creation',
'no' => 'no_ldap_account_creation'),
'yes');
return $settings;
}
// ----------------------
/**
* Called by the member_member_login_start hook
*/
function member_member_login_start()
{
return $this->login_authenticate_start();
}
// ----------------------
/**
* Called by the login_authenticate_start hook
*/
function login_authenticate_start()
{
$user_info = array();
$user_info['username'] = ee()->input->post('username', TRUE);
$user_info['password'] = ee()->input->post('password', TRUE);
$result = $this->authenticate_user($user_info);
$everything = array_merge($result, $user_info);
if ($this->debug)
{
echo 'Dump of varible:';
echo'<pre>';
var_dump($everything);
echo'</pre>';
}
if ($result['authenticated'])
{
$this->debug_print('Authenticated. Trying to sync \''.$user_info['username'].'\' with EE member system...');
$this->sync_user_details($everything);
}
else
{
$this->debug_print('Could not authenticate username \''.$user_info['username'].'\' with LDAP');
}
//$this->close_connection($connection);
if ($this->debug)
exit();
}
// ----------------------
function sync_user_details($user_info)
{
// Sync EE password to match LDAP (if account exists)
$user_info['encrypted_password'] = hash('sha1',stripslashes($user_info['password']));
// Get the password information from Auth
$this->EE->load->library('auth');
$user_info['hashed_password'] = $this->EE->auth->hash_password($user_info['password']);
$sql = 'UPDATE exp_members SET password = \''.$this->EE->db->escape_str($user_info['encrypted_password']).'\' WHERE username = \''.$this->EE->db->escape_str($user_info['username']).'\'';
$this->debug_print('Updating user with SQL: '.$sql);
$this->EE->db->query($sql);
// now we might want to do some EE account creation
if ($this->settings['use_ldap_account_creation'] === 'yes')
{
$this->debug_print('Attempting to create EE user...');
$this->create_ee_user($user_info);
}
/* Add a check if they are alumni or not */
if( isset($user_info['edupersonaffiliation'][0]) OR $user_info['edupersonaffiliation'][0] != "")
{ /* Do nothing */
} else {
// Set the user group to be for former studnets/faculy/staff
$sql = 'UPDATE exp_members SET group_id = 7 WHERE username = \''.$this->EE->db->escape_str($user_info['username']).'\'';
$this->debug_print('Member affiliation could not be found, so user must not be active. Setting group. Updating using query: '.$sql);
$this->EE->db->query($sql);
}
}
// ---------------------- This section was heavily modified in order to handle the two directories. Outside the tree, this is the only way to do this.
function create_ee_user($user_info)
{
$sql = 'SELECT \'username\' FROM exp_members WHERE username = \''.$this->EE->db->escape_str($user_info['username']).'\'';
$this->debug_print('Checking for existing user with SQL: '.$sql);
$query = $this->EE->db->query($sql);
// user doesn't exist in exp_members table, so we will create an EE account
if ($query->num_rows === 0)
{
$this->debug_print('Using LDAP for account creation...');
$data['username'] = $user_info['username'];
$data['password'] = $user_info['encrypted_password'];
$data['email'] = $user_info['mail'][0];
//$data['salt'] = $user_info['hashed_password'];
$data['ip_address'] = $this->EE->input->ip_address();
$data['unique_id'] = $this->EE->functions->random('encrypt');
$data['crypt_key'] = $this->EE->functions->random('encrypt', 16);
$data['join_date'] = $this->EE->localize->now;
$data['language'] = $this->EE->config->item('deft_lang');
$data['timezone'] = $this->EE->config->item('default_site_timezone');
$data['time_format'] = $this->EE->config->item('time_format') ? $this->EE->config->item('time_format') : 'us';
$data['screen_name'] = $user_info['givenname'][0]." ".$user_info['sn'][0];
//$custom_data['a_field'] = $user_info['edupersonaffiliation'][0] . ',' . $user_info['edupersonaffiliation'][1];
$custom_data['first_name'] = $user_info['givenname'][0];
$custom_data['last_name'] = $user_info['sn'][0];
// Eval if user is student or staff
if ($user_info['i_am_a'] == 'student')
{
//ExpressionEngine group ID = Students
$data['group_id'] = $this->$settings['groupID_student'];
}
if ($user_info['i_am_a'] == 'staff')
{
//ExpressionEngine group ID = staff
$data['group_id'] = $this->$settings['groupID_facultystaff'];
}
if ($user_info['i_am_a'] == '')
{
//ExpressionEngine group ID = Guest
$data['group_id'] = $this->$settings['groupID_alumni'];
}
// add in any optional data. Name of field => Value it needs to be. Set it ahead of time.
$opt_data = array(
//'m_field_id_14' => $custom_data['a_field'],
'm_field_id_4' => $custom_data['first_name'],
'm_field_id_5' => $custom_data['last_name'],
);
$this->debug_print('Inserting user with data:<pre> '.print_r($data, TRUE).'</pre><br /><pre>'.print_r($custom_data, TRUE).'</pre><br /><pre>'.print_r($opt_data, TRUE).'</pre>');
$this->EE->load->model('member_model');
$member_id = $this->EE->member_model->create_member($data, $opt_data);
$this->debug_print('Member ID: '.$member_id);
if ($member_id > 0) // update other relevant fields
{
//$sql = 'UPDATE exp_members SET photo_filename = \'photo_'.$member_id.'.jpg\', photo_width = \'90\', photo_height = \'120\'';
//$query = $this->EE->db->query($sql);
}
else
{
exit('Could not create user account for '.$user_info['username'].'<br/>'."\n");
}
}
$this->debug_print('Done with create ee user function, moving on.');
}
// ----------------------
function authenticate_user($user_info)
{
$login_settings = array();
// First try the students directory.
$login_settings['ds'] = ldap_connect("students.ad.unt.edu", 389);
$login_settings['dn'] = "dc=students,dc=ad,dc=unt,dc=edu";
$login_settings['search_dir'] = 'student';
$bind_result = ldap_bind($login_settings['ds'] , $user_info['username'].'@students.ad.unt.edu', $user_info['password']);
$this->debug_print("Student bind result: $bind_result");
//If the login was correct, assume they were a student with the correct login.
if ($bind_result){
return $this->greedy_ldap_grab($login_settings, $user_info);
}
// If it didn't work, then try the other directory.
if (!$bind_result) {
$this->debug_print('Bind not succesfull to student directory, moving to the faculty-staff directory.');
// Connect to unt.ad.unt.edu
$login_settings['ds'] = ldap_connect("unt.ad.unt.edu", 389);
$login_settings['dn'] = "dc=unt,dc=ad,dc=unt,dc=edu";
$login_settings['search_dir'] = 'staff';
$bind_result = ldap_bind($login_settings['ds'] , $user_info['username'].'@unt.ad.unt.edu', $user_info['password']);
$this->debug_print("UNT bind result: $bind_result");
if ($bind_result)
{
return $this->greedy_ldap_grab($login_settings, $user_info);
} else {
$this->debug_print("Not able to bind to either directory.");
}
}
}
// ----------------------
function greedy_ldap_grab($login_settings, $user_info)
{
$this->debug_print('Starting Greedy LDAP Grab...');
$filter = "(cn={$user_info['username']})";
$this->debug_print('Filter: ' . $filter);
// The fields to pull from the directory entry.
$attributes = array('givenName','mail','sn','cn','edupersonaffiliation');
// Actually do the search of the user, and pull the above info.
$result = ldap_search($login_settings['ds'], $login_settings['dn'], $filter);
$this->debug_print("Result: {$result}");
// If the search comes up empty, end and report the error.
if (ldap_count_entries($login_settings['ds'], $result) != 1)
{
return array('authenticated' => false);
}
// If no error searching:
// Get all the entries that match.
$info = ldap_get_entries($login_settings['ds'], $result);
// Since there could be more than one, only use the first entry it finds.
$user_info = $info[0];
$this->debug_print('Data for '.$info["count"].' items returned<br/>');
// If everything goes well, then you are assigned a value.
$user_info['i_am_a'] = $login_settings['search_dir'];
$this->debug_print("You are a ".$login_settings['search_dir']);
$user_info['authenticated'] = true;
//Close Connection
$this->debug_print('Closing connection...');
ldap_close($login_settings['ds']) or
die('Could not close the LDAP connection<br/>'."\n");
$this->debug_print('Returning user info from Greedy.');
return $user_info;
}
/** ORG
function authenticate_user($conn, $username, $password, $ldap_username_attribute, $ldap_search_base)
{
$this->debug_print('Searching for attribute '.$ldap_username_attribute.'='.$username.' ...');
// Search username entry
$result = ldap_search($conn, $ldap_search_base, $ldap_username_attribute.'='.$username);
$this->debug_print('Search result is: '.$result);
// Search not successful (server down?), so do nothing - standard MySQL authentication can take over
if ($result === FALSE)
{
$this->EE->session->userdata['ldap_message'] = $this->settings['no_ldap_login_message'];
return array('authenticated' => false);
}
$this->debug_print('Number of entires returned is '.ldap_count_entries($conn, $result));
// username not found, so do nothing - standard MySQL authentication can take over
if (ldap_count_entries($conn, $result) < 1)
{
return array('authenticated' => false);
}
$this->debug_print('Getting entries for \''.$username.'\' ...');
$info = ldap_get_entries($conn, $result); // entry for username found in directory, retrieve entries
$user_info = $info[0];
$this->debug_print('Data for '.$info["count"].' items returned<br/>');
$user_info['username'] = $username;
$user_info['password'] = $password;
// Authenticate LDAP user against password submitted on login
$dn = $user_info['dn'];
$success = @ldap_bind($conn, $dn, $this->ldap_encode($password)); // bind with user credentials
if (!$success)
{
$this->debug_print('Error binding with supplied password (dn: '.$dn.') ERROR: '.ldap_error($conn));
}
$user_info['authenticated'] = $success;
return $user_info;
}
**/
function debug_print($message, $br="<br/>\n")
{
if ($this->debug)
{
if (is_array($message))
{
print('<pre>');
print_r($message);
print('</pre>'.$br);
}
else
{
print($message.' '.$br);
}
}
}
function ldap_encode($text)
{
return iconv("UTF-8", $this->settings['ldap_character_encode'], $text);
}
}
// END CLASS Nce_ldap
/* End of file ext.nce_ldap.php */
/* Location: ./system/expressionengine/third_party/nce_ldap/ext.nce_ldap.php */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment