OpenCart Admin LDAP Module

Today I was looking for an LDAP module for the OpenCart Admin page. Since I didn’t find anyone, I decided to build my own.

The script may be quick and dirty, but it works for me. Please feel free to edit the script if you need to.

UPDATE: Since the release of Open Cart 2.0 I have modified the script to work with the new version. Below you will find a script for both version 1.5.x and version 2.0!

This is some kind of a basic step-by-step guide of what the script does:

  1. The user logging in at the OpenCart Admin page.
  2. The script tries to authenticate against the LDAP server with the users credentials.
  3. If the authentication succeeds, the script verifies if the user is member of the correct LDAP group.
  4. If yes, the script looks in the OpenCart database and tries to find the user.
  5. If the user does not exists, the script creates the user and logs in – If the user exists, the script syncs the information (such as name and email) with LDAP directory and logs in.

So how about security?

The script will never store the users LDAP password in the database. When the user is created, a random password is stored in the database.  Though, I don’t know any other way for OpenCart to actually login without provide the correct password (as in the database), so I’ve made the script to get the current password and store it in a variable, then hash it and update the database and finally run OpenCart’s built-in login function and provide the password as of before hashing it.

Since OpenCart’s built-in login function will hash the provided password and then compare it with the one stored in the database, the login will succeed!

One benefit with this is that every time a user logs in to the OpenCart Admin page, the password in the database will actually be changed, making it harder for an hacker to brute-force it.

Below you find the actual script. You have to place the script in the following file: /opencart_root/admin/controller/common/login.php, just below this:

protected function validate() {

You also have to comment out the following (OpenCarts built-in login function):

if (!isset($this->request->post['username']) || !isset($this->request->post['password']) || !$this->user->login($this->request->post['username'], $this->request->post['password'])) {
    $this->error['warning'] = $this->language->get('error_login');
}

if (!$this->error) {
    return true;
} else {
    return false;
}

So, to the actual script:

For version 1.5.x

//-----------------------------------------------------------------
//---------------------- LDAP authentication ----------------------
//-----------------------------------------------------------------
		
$ldap_host = "10.0.0.2"; //LDAP Server
$ldap_dn = "ou=Users,dc=domain,dc=local"; //DN to look for users in
$ldap_domain = "domain.local"; //LDAP domain
$ldap_group = "AD Group"; //LDAP group user has to be member of
		
//Connect to LDAP server
$ldap = ldap_connect($ldap_host);
		
//Verify username and password
if($bind = @ldap_bind($ldap, $this->request->post['username'] . "@" . $ldap_domain, $this->request->post['password'])){
	$filter = "(sAMAccountName=" . $this->request->post['username'] . ")";
	$attribute = array("memberof", "mail", "givenname", "sn");
	$result = ldap_search($ldap, $ldap_dn, $filter, $attribute);
	$entries = ldap_get_entries($ldap, $result);
 	ldap_unbind($ldap);
		
	//Check if user is member of correct group
	foreach($entries[0]['memberof'] as $groups){
		if(strpos($groups, $ldap_group)){
					
			//Define variables
			$username = $this->request->post['username'];
			$salt = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 9); //Generate a new salt
			$firstname = utf8_encode($entries['0']['givenname']['0']);
			$lastname = utf8_encode($entries['0']['sn']['0']);
			$email = $entries['0']['mail']['0'];
			$ip = $_SERVER['REMOTE_ADDR'];
						
			//If the user already exists in the database
			$query = mysql_query("SELECT * FROM oc_user WHERE username = '" . $this->request->post['username'] . "'");
			if(mysql_num_rows($query) >= 1){
						
				//Update information from LDAP
				mysql_query("UPDATE oc_user SET firstname = '$firstname', lastname = '$lastname', email = '$email', ip = '$ip' WHERE username = '$username'") or die(mysql_error());
						
				//Get the current password (hash)
				$password = mysql_query("SELECT * FROM oc_user WHERE username = '$username'");
				$password = mysql_fetch_array($password, MYSQL_ASSOC);
				$password = $password['password'];
						
				//Generate a new password (hash the old one)
				//Also update salt just in case (is salt in use?)
				mysql_query("UPDATE oc_user SET password = '" . md5($password) . "', salt = '$salt' WHERE username = '$username'");
						
				//Since we know the new password unhashed, we can login
				$this->user->login($this->request->post['username'], $password);
				return true;
						
			//If the user does not exists in the database
			} else {
						
				//Generate a password and hash it
				$password = md5(substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 40));
						
				//Create the user
				mysql_query("INSERT INTO oc_user (user_group_id, username, password, salt, firstname, lastname, email, ip, status) VALUES (1, '$username', '$password', '$salt', '$firstname', '$lastname', '$email', '$ip', 1)") or die(mysql_error());
						
				//Generate a new password (hash the old one)
				mysql_query("UPDATE oc_user SET password = '" . md5($password) . "' WHERE username = '$username'");
						
				//Since we know the new password unhashed, we can login
				$this->user->login($this->request->post['username'], $password);
				return true;
			}
		}
	}
} else {
		
	//Login failed
	$this->error['warning'] = $this->language->get('error_login');
	return false;
}
		
//-----------------------------------------------------------------
//-----------------------------------------------------------------

For version 2.0

//-----------------------------------------------------------------
//---------------------- LDAP authentication ----------------------
//-----------------------------------------------------------------
		
$ldap_host = "10.0.0.2"; //LDAP Server
$ldap_dn = "ou=Users,dc=domain,dc=local"; //DN to look for users in
$ldap_domain = "domain.local"; //LDAP domain
$ldap_group = "AD Group"; //LDAP group user has to be member of
		
//Connect to LDAP server
$ldap = ldap_connect($ldap_host);
		
//Verify username and password
if($bind = @ldap_bind($ldap, $this->request->post['username'] . "@" . $ldap_domain, $this->request->post['password'])){
	$filter = "(sAMAccountName=" . $this->request->post['username'] . ")";
	$attribute = array("memberof", "mail", "givenname", "sn");
	$result = ldap_search($ldap, $ldap_dn, $filter, $attribute);
	$entries = ldap_get_entries($ldap, $result);
	ldap_unbind($ldap);
		
	//Check if user is member of correct group
	foreach($entries[0]['memberof'] as $groups){
		
		if(strpos($groups, $ldap_group)){
			
			//Define variables
			$username = $this->request->post['username'];
			$salt = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 9); //Generate a new salt
			$firstname = utf8_encode($entries['0']['givenname']['0']);
			$lastname = utf8_encode($entries['0']['sn']['0']);
			$email = $entries['0']['mail']['0'];
			$ip = $_SERVER['REMOTE_ADDR'];
			
			//Connect to SQL
			$dbconnect = new mysqli(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
						
			//If the user already exists in the database
			$query = $dbconnect->query("SELECT * FROM oc_user WHERE username = '" . $this->request->post['username'] . "'");
			if($query->num_rows >= 1){
						
				//Update information from LDAP
				$dbconnect->query("UPDATE oc_user SET firstname = '" . $firstname . "', lastname = '" . $lastname . "', email = '" . $email . "', ip = '" . $ip . "' WHERE username = '" . $username . "'");
				
				//Get the current password (hash)
				$password = $dbconnect->query("SELECT * FROM oc_user WHERE username = '" . $username . "'");
				$password = $password->fetch_array(MYSQLI_ASSOC);
				$password = $password['password'];
				
				//Generate a new password (hash the old one)
				//Also update salt just in case (is salt in use?)
				$dbconnect->query("UPDATE oc_user SET password = '" . md5($password) . "', salt = '" . $salt . "' WHERE username = '" . $username . "'");
						
				//Since we know the new password unhashed, we can login
				$this->user->login($this->request->post['username'], $password);
				return true;
						
			//If the user does not exists in the database
			} else {
								
				//Generate a password and hash it
				$password = md5(substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 40));
								
				//Create the user
				$dbconnect->query("INSERT INTO oc_user (user_group_id, username, password, salt, firstname, lastname, email, ip, status) VALUES (1, '" . $username . "', '" . $password . "', '" . $salt . "', '" . $firstname . "', '" . $lastname . "', '" . $email . "', '" . $ip . "', 1)");
								
				//Generate a new password (hash the old one)
				$dbconnect->query("UPDATE oc_user SET password = '" . md5($password) . "' WHERE username = '" . $username . "'");
								
				//Since we know the new password unhashed, we can login
				$this->user->login($this->request->post['username'], $password);
				return true;
			}
		}
	}
} else {
		
	//Login failed
	$this->error['warning'] = $this->language->get('error_login');
	return false;
}
		
$dbconnect->close;

//-----------------------------------------------------------------
//-----------------------------------------------------------------

18 thoughts on “OpenCart Admin LDAP Module”

  1. This is awesome. But I have a question with the following:

    ‘If the user does not exists, the script creates the user and logs in ‘

    How does this keep anyone from deliberately creating a random user and logging into your store?

    1. Hi jamal!

      If the user exists in the LDAP directory (and the user is member of the correct LDAP group) and the credentials is correct, the script will look in the OpenCarts user table – if the user exists it will just accept the login, but if the user does not exists the user will be added to OpenCarts user table.

      In other words, if the user is not successfully authenticated through the LDAP directory, the login attempt will fail.

  2. What if you have multiple OU’s the users are in? If I place the users in the Group and point the OU at the $ldap_dn at the OU the group is in will that work?

    1. Hi Danny!

      To get it work with multiple OUs you have to understand how LDAP works.
      When you point at an OU, LDAP will always look in all OUs under it.

      Lets say you have one OU called Admins and one OU called Users. Then you can point LDAP to look in dc=domain,dc=local (to look in your hole domain).

      Hope it helps!

  3. Awesome Thank you.

    I am still having some problems however. I am using Windows Server 2012 R2 with IIS 8.5 with OpenCart 1.5.6

    I have used your .php file and adjusted the

    $ldap_host = “xxx.xxx.xxx.xxx”; //LDAP Server
    $ldap_dn = “dc=mydomain,dc=org”; //DN to look for users in
    $ldap_domain = “mydomain.org”; //LDAP domain
    $ldap_group = “OpenCart”; //LDAP group user has to be member of

    Areas. When I attempt to log into the admin panel it goes to a white screen and hangs. When I attempt to log into the front side with an account that is not in OpenCart it tells “Warning: No match for E-Mail Address and/or Password.”

    Any thoughts?

    1. If you get a blank screen something is missing in IIS. I have’t work with IIS that much but try to find a log file. Perhaps you have to activate/install LDAP module.

    1. Notice: Undefined offset: 0 in /var/www/html/Store/admin/controller/common/login.php on line 101Warning: Invalid argument supplied for foreach() in /var/www/html/Store/admin/controller/common/login.php on line 101Notice: Undefined variable: dbconnect in /var/www/html/Store/admin/controller/common/login.php on line 175Notice: Trying to get property of non-object in /var/www/html/Store/admin/controller/common/login.php on line 175

    2. That is not an error, just a notice. This is normal.
      Change your error level in php.ini to not include notices.

  4. It won’t log me in with my admin credentials with the new login.php and it is not auto creating users in the database when I attempt to login either. is there a way you can email me and I can send the entire login.php files and you can see if I have something set wrong? is there any specific modules or daemons I should have running in Linux for this to work outside of what I needed for opencart 2?

  5. Great page! Very informative 🙂

    I was wondering how much modification is need to use LDAP Auth for customer logins?

    The /opencart_root/catalog/controller/account/login.php seems very similar, but customers login with email addresses. I don’t mind them login with their LDAP accounts if we can get this to work 🙂

    Any thoughts?

    Thanks!

  6. Lucas,
    Thanks for posting this, very helpful. I did have a few issues initially getting this to work for my implementation, mainly due to the LDAP query needed, but also for the insert statement which now requires additional mandatory fields, at least for 2.1.0.2. My updated query is here:

    $dbconnect->query(“INSERT INTO oc_user (user_group_id, username, password, salt, firstname, lastname, email, ip, image, code, status, date_added) VALUES (1, ‘” . $username . “‘, ‘” . $password . “‘, ‘” . $salt . “‘, ‘” . $firstname . “‘, ‘” . $lastname . “‘, ‘” . $email . “‘, ‘” . $ip . “‘, ”, ”, 1, NOW())”);

    I also implemented this for the customer login as was requested above. The file is:
    catalog/controller/account/login.php

    Andrew

    protected function validate() {
    $this->event->trigger(‘pre.customer.login’);

    // Check how many login attempts have been made.
    $login_info = $this->model_account_customer->getLoginAttempts($this->request->post[’email’]);

    if ($login_info && ($login_info[‘total’] >= $this->config->get(‘config_login_attempts’)) && strtotime(‘-1 hour’) error[‘warning’] = $this->language->get(‘error_attempts’);
    }

    // Check if customer has been approved.
    $customer_info = $this->model_account_customer->getCustomerByEmail($this->request->post[’email’]);

    if ($customer_info && !$customer_info[‘approved’]) {
    $this->error[‘warning’] = $this->language->get(‘error_approved’);
    }

    if (!$this->error) {
    //—————————————————————–
    //———————- LDAP authentication ———————-
    //—————————————————————–

    $ldap_host = “10.203.178.200”; //LDAP Server
    $ldap_dn = “dc=myserver,dc=local”; //DN to look for users in
    $ldap_domain = “myserver”; //LDAP domain
    $ldap_group = “Hosting”; //LDAP group user has to be member of

    //Connect to LDAP server
    $ldap = ldap_connect($ldap_host);
    ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

    //Verify username and password
    if($bind = @ldap_bind($ldap, $this->request->post[’email’], $this->request->post[‘password’])){
    $filter = “(mail=” . $this->request->post[’email’] . “)”;
    $attribute = array(“memberof”, “mail”, “givenname”, “sn”);
    $result = ldap_search($ldap, $ldap_dn, $filter,$attribute);
    $entries = ldap_get_entries($ldap, $result);

    ldap_unbind($ldap);

    //Check if user is member of correct group
    foreach($entries[0][‘memberof’] as $groups){

    if(strpos($groups, $ldap_group)){
    //Define variables
    $username = $this->request->post[’email’];
    $salt = substr(str_shuffle(“0123456789abcdefghijklmnopqrstuvwxyz”), 0, 9); //Generate a new salt
    $firstname = utf8_encode($entries[‘0’][‘givenname’][‘0’]);
    $lastname = utf8_encode($entries[‘0’][‘sn’][‘0’]);
    $email = $entries[‘0’][‘mail’][‘0’];
    $ip = $_SERVER[‘REMOTE_ADDR’];

    //Connect to SQL
    $dbconnect = new mysqli(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
    //If the user already exists in the database
    $query = $dbconnect->query(“SELECT * FROM oc_customer WHERE email = ‘” . $this->request->post[’email’] . “‘”);
    if($query->num_rows >= 1){

    //Update information from LDAP
    $dbconnect->query(“UPDATE oc_customer SET firstname = ‘” . $firstname . “‘, lastname = ‘” . $lastname . “‘, email = ‘” . $email . “‘, ip = ‘” . $ip . “‘ WHERE email = ‘” . $username . “‘”);

    //Get the current password (hash)
    $password = $dbconnect->query(“SELECT * FROM oc_customer WHERE email = ‘” . $username . “‘”);
    $password = $password->fetch_array(MYSQLI_ASSOC);
    $password = $password[‘password’];

    //Generate a new password (hash the old one)
    //Also update salt just in case (is salt in use?)
    $dbconnect->query(“UPDATE oc_customer SET password = ‘” . md5($password) . “‘, salt = ‘” . $salt . “‘ WHERE email = ‘” . $username . “‘”);

    //Since we know the new password unhashed, we can login
    $this->customer->login($this->request->post[’email’], $password);
    $this->model_account_customer->deleteLoginAttempts($this->request->post[’email’]);
    $dbconnect->close;

    return !$this->error;

    //If the user does not exists in the database
    } else {
    //Generate a password and hash it
    $password = md5(substr(str_shuffle(“0123456789abcdefghijklmnopqrstuvwxyz”), 0, 40));

    //Create the user
    $dbconnect->query(“INSERT INTO oc_customer (customer_group_id, firstname, lastname, email, telephone, fax, password, salt, custom_field, ip, status, approved, safe, token, date_added) VALUES (1, ‘” . $firstname . “‘, ‘” . $lastname . “‘, ‘” . $email . “‘, ”, ”, ‘” . $password . “‘, ‘” . $salt . “‘, ”, ‘” . $ip . “‘, 1, 1, 0, ”, NOW())”);

    //Generate a new password (hash the old one)
    $dbconnect->query(“UPDATE oc_customer SET password = ‘” . md5($password) . “‘ WHERE email = ‘” . $username . “‘”);

    //Since we know the new password unhashed, we can login
    $this->customer->login($this->request->post[’email’], $password);
    $this->model_account_customer->deleteLoginAttempts($this->request->post[’email’]);
    $dbconnect->close;

    return !$this->error;
    }
    }
    }
    } else {
    //Login failed
    $this->error[‘warning’] = $this->language->get(‘error_login’);
    }

    $dbconnect->close;
    $this->model_account_customer->addLoginAttempt($this->request->post[’email’]);

    /*if (!$this->customer->login($this->request->post[’email’], $this->request->post[‘password’])) {
    $this->error[‘warning’] = $this->language->get(‘error_login’);

    $this->model_account_customer->addLoginAttempt($this->request->post[’email’]);
    } else {
    $this->model_account_customer->deleteLoginAttempts($this->request->post[’email’]);
    }*/
    }

    return !$this->error;
    }
    }

    1. I haven’t actually used OpenCart for a long time. I guess it should work, but a few modifications might be needed. You should give it a try!

Leave a Reply to Danny Cancel reply

Your email address will not be published. Required fields are marked *