Location: PHPKode > projects > Timesheet > timesheet.php/class.AuthenticationManager.php
<?php

error_reporting(E_ALL);
ini_set('display_errors', true);

require("table_names.inc");
require("common.inc");
require("timezone.inc");
require("enum.php");

//define constants for error code
enum(
	"AUTH_NONE", //no attempt has been made to authenticate yet
	"AUTH_SUCCESS", //authentication succeeded
	"AUTH_FAILED_INCORRECT_PASSWORD", //incorrect password
	"AUTH_FAILED_NO_USERNAME_PASSED", //error no username was passed
	"AUTH_FAILED_EMPTY_PASSWORD", //error empty password not allowed
	"AUTH_LOGOUT", //user logged out	
	"AUTH_FAILED_LDAP_LOGIN", //failed login via LDAP, check ldapErrorCode
	"AUTH_FAILED_NO_LDAP_MODULE" //no ldap module detected
);

//define constants for ldap error code
enum (
	"LDAP_AUTH_NONE", //no LDAP authentication has been attempted
	"LDAP_CONNECTION_FAILED", //connection failed
	"LDAP_MULTIPLE_ENTRIES_RETURNED", //multiple entries were returned
	"LDAP_SERVER_ERROR", //server error, check server error code
	"LDAP_USER_NOT_FOUND" //user not found
);

//define clearance levels
define("CLEARANCE_ADMINISTRATOR", 10);	

/**
*	Manages and provides authentication services
*/
class AuthenticationManager {

	/**
	*	The error code
	*/
	var $errorCode = AUTH_NONE;
	
	/**
	* The error text
	*/
	var $errorText = "Authentication has not yet been attempted";

	/**
	*	The error code to check if errorCode=AUTH_FAILED_LDAP_LOGIN 
	*/
	var $ldapErrorCode;
	
	/**
	*	The error text which matches the ldapErrorCode
	*/
	var $ldapErrorText;
	
	/**
	* The error code returned from the LDAP server
	*/
	var $ldapServerErrorCode;
	
	/**
	* The error description returned from the LDAP server
	*/
	var $ldapServerErrorText;	
	
	/* authentication function: this is called by
	*   each page to ensure that there is an authenticated user
	*/
	function login($username, $password) {
		require("table_names.inc");			
		require("database_credentials.inc");
				
		//start/continue the session		
		session_start();

		//set initial error codes		
		$this->errorCode = AUTH_NONE;
		$this->errorText = "No attempt has been made to authenticate yet";
		$this->ldapErrorCode = LDAP_AUTH_NONE;
		$this->ldapErrorText = "No attempt has been made to authenticate via LDAP yet";
		$this->ldapServerErrorCode = 0;
		$this->ldapServerErrorText = "[]";
		
		//a username must be passed
		if (empty($username)) {
			$this->logout();
			$this->errorCode = AUTH_FAILED_NO_USERNAME_PASSED;
			$this->errorText = "You must enter a username";
			return false;
		}

		//a password must be passed
		if (empty($password)) {
			$this->logout();
			$this->errorCode = AUTH_FAILED_EMPTY_PASSWORD;
			$this->errorText = "You must enter a password";
			return false;
		}
				
		//connect to the database		
		$dbh = dbConnect();				
		
		//check whether we are using ldap
		if ($this->usingLDAP()) {
			//check that the module is availble
			$ldapMaxLinks = ini_get("ldap.max_links");
			if (empty($ldapMaxLinks)) {					
				$this->errorCode = AUTH_FAILED_NO_LDAP_MODULE;
				$this->errorText = "Could not access LDAP module - is it installed?";
				return false;			
			}
		
			//check their credentials with LDAP
			if (!$this->ldapLogin($username, $password)) {			
				$this->errorCode = AUTH_FAILED_LDAP_LOGIN;
				$this->errorText = "Authentication via LDAP failed";
				return false;
			}
		}
		else {
			//query the user table for authentication details			
			list($qh,$num) = dbQuery("SELECT password AS passwd1, $DATABASE_PASSWORD_FUNCTION('$password') AS passwd2 ".
		                         "FROM $USER_TABLE WHERE username='$username'");
			$data = dbResult($qh);
			
			//is the password correct?
			if ($num == 0 || $data["passwd1"] != $data["passwd2"]) {
				$this->errorCode = AUTH_FAILED_INCORRECT_PASSWORD;
				$this->errorText = "Incorrect username or password";
				return false;
			}
		}

		//get the access level
		list($qh,$num) = dbQuery("SELECT level ".
		                         "FROM $USER_TABLE WHERE username='$username'");
		$data = dbResult($qh);

		//set session variables
		$_SESSION["loggedInUser"] = $username;
		$_SESSION["accessLevel"] = $data["level"];
		$_SESSION["contextUser"] = $username;
	  
		$this->errorCode = AUTH_SUCCESS;
		$this->errorText = "Authentication succeeded";
		
		return true;
	}
	
	/**
	* Logs out the currenlty logged in user
	*/
	function logout() {
		//start/continue the session		
		session_start();

		//unset all the variables
		session_unset();
	
		//destroy the session
		session_destroy();

		$this->errorCode = AUTH_LOGOUT;
		$this->errorText = "The user was logged out";									
		return;	
	}

	/**
	*	returns true if the user is logged in
	*/	
	function isLoggedIn() {
		//start/continue the session		
		@session_start();

		return !empty($_SESSION['accessLevel']) && !empty($_SESSION['loggedInUser']) && !empty($_SESSION['contextUser']);
	}
	
	/**
	* returns true if the user has clearance to the specified level
	*/
	function hasClearance($accessLevel) {
		//start/continue the session		
		@session_start();

		return (isset($_SESSION['accessLevel']) && $_SESSION['accessLevel'] >= $accessLevel);
	}	

	/* This function returns true if the system is configured to use 
	*	LDAP for authentication 
	*/
	function usingLDAP() {
		require("table_names.inc");
		list($qh, $num) = dbQuery("SELECT useLDAP FROM $CONFIG_TABLE WHERE config_set_id='1'");
		$data = dbResult($qh);	
		return $data['useLDAP'] == 1;
	}
	
	function ldapLogin($username, $password) {
		require("table_names.inc");
		//get the connection settings from the database
		list($qh, $num) = dbQuery("SELECT LDAPScheme, LDAPHost, LDAPPort, LDAPBaseDN, " .
															"LDAPUsernameAttribute, LDAPSearchScope, LDAPFilter, LDAPProtocolVersion, " .
															"LDAPBindUsername, LDAPBindPassword " .
															"FROM $CONFIG_TABLE WHERE config_set_id='1'");
		$data = dbResult($qh);
		
		//build up connection string
		$connectionString = $data['LDAPScheme'] . "://" . $data['LDAPHost'] . ":" . $data['LDAPPort'];	
		
		//connect to server
		//echo "connecting to server: $connectionString <p>";
		if (!($connection = @ldap_connect($connectionString))) {		
			$this->ldapErrorCode = LDAP_CONNECTION_FAILED;	
			$this->ldapErrorText = "Failed to connect to ldap server at $connectionString";
			return false;
		}

		//attempt to set the protocol version to use   	
		@ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $data["LDAPProtocolVersion"]);

		//do we need to bind anonymously?
		if (empty($data["LDAPBindUserName"])) {
			//bind to server (anonymously)
			if (!($bind = @ldap_bind($connection))) {
				$this->ldapErrorCode = LDAP_SERVER_ERROR;
				$this->ldapServerErrorCode = ldap_errno($connection);
				$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);
				return false;
			}
		}
		else {
			//bind to server (with username and password)
			if (!($bind = @ldap_bind($connection, $data["LDAPBindUsername"], $data["LDAPBindPassword"]))) {
				$this->ldapErrorCode = LDAP_SERVER_ERROR;
				$this->ldapServerErrorCode = ldap_errno($connection);
				$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);
				return false;
			}						
		}
  
		//attempt to set the protocol version to use   	
		@ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $data["LDAPProtocolVersion"]);
	
		//build up the filter by adding the username filter
		$filter = $data['LDAPUsernameAttribute'] . "=" . $username;
		if ($data['LDAPFilter'] != "") {
			//does it start with a '(' and end with a ')' ?
			$userFilter = $data["LDAPFilter"];
			$length = strlen($userFilter);
			if ($userFilter{0} == "(" && $userFilter{$length-1} == ")")
				$userFilter = substr($userFilter, 1, $length-2);				
		
			$filter = "(&(" . $userFilter . ")(" . $filter . "))";
		}

		if ($data["LDAPSearchScope"] == "base") {
			//search the directory returning records in the base dn
			//echo "<p>searching base dn: $data[LDAPBaseDN]        with filter: $filter <p>";
			if (!($search = @ldap_read($connection, $data['LDAPBaseDN'], $filter))) {
				$this->ldapErrorCode = LDAP_SERVER_ERROR;
				$this->ldapServerErrorCode = ldap_errno($connection);
				$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);	
				return false;
			}
		}
		else if ($data["LDAPSearchScope"] == "one") {				
			//search the directory returning records in the base dn
			//echo "<p>searching base dn: $data[LDAPBaseDN]        with filter: $filter <p>";
			if (!($search = @ldap_list($connection, $data['LDAPBaseDN'], $filter))) {
				$this->ldapErrorCode = LDAP_SERVER_ERROR;
				$this->ldapServerErrorCode = ldap_errno($connection);
				$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);	
				return false;
			}
		}
		else { //full subtree search
			//search the directory returning records in the base dn
			//echo "<p>searching base dn: $data[LDAPBaseDN]        with filter: $filter <p>";
			if (!($search = @ldap_search($connection, $data['LDAPBaseDN'], $filter))) {
				$this->ldapErrorCode = LDAP_SERVER_ERROR;
				$this->ldapServerErrorCode = ldap_errno($connection);
				$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);	
				return false;
			}
		}
     
		//get the results
		$numberOfEntries = ldap_count_entries($connection,$search);
	 
	 	if ($numberOfEntries == 0) {
			$this->ldapErrorCode = LDAP_USER_NOT_FOUND;
			$this->ldapErrorText = "The user was not found in the LDAP database";
			return false;
	 	}
	 
		//there must be 1 and only 1 result
		if ($numberOfEntries > 1) {
			$this->ldapErrorCode = LDAP_MULTIPLE_ENTRIES_RETURNED;
			$this->ldapErrorText = "Multiple entries were returned for that username";
			return false;
		}
 
		//get the entry
		$entry = ldap_first_entry($connection, $search);
				
		//get the entries dn
		$entryDN = ldap_get_dn($connection, $entry);
		//print "<p>The entry was found and its DN is '" . $entryDN . "'</p>";
		
		//now try a bind with this dn and the password
		if (!($userBind = @ldap_bind($connection, $entryDN, $password))) {
			$this->ldapErrorCode = LDAP_SERVER_ERROR;
			$this->ldapServerErrorCode = ldap_errno($connection);
			$this->ldapServerErrorText = "LDAP: " . ldap_error($connection);
			return false;
		}
								
		//get the attributes for this entry
		$attributes = ldap_get_attributes($connection, $entry);
										
		//get some info from the first entry to update into the db		
		$lastName = $attributes['sn'][0];
		if (!isset($attributes['givenName'])) {
			if (isset($attributes['cn'])) {
				$spacePos = strpos($attributes['cn'][0], " ");
				if (!($spacePos === false))
					$firstName = substr($attributes['cn'][0], 0, $spacePos);
				else
					$firstName = $attributes['cn'][0];				
			}
			else 
				$firstName = $lastName;				
		}
		else
			$firstName = $attributes['givenName'][0];
		
		$phone = isset($attributes['telephoneNumber']) ? $attributes['telephoneNumber'][0]: "";
		$emailAddress = isset($attributes['mail']) ? $attributes['mail'][0]: "";
		$billRate = 100;
				
		//does the user exist in the db?
		if (!$this->userExists($username)) {
			//create the user
			dbquery("INSERT INTO $USER_TABLE (username, level, password, first_name, last_name, " .
						"email_address, phone, bill_rate, time_stamp, status) " . 
						"VALUES ('$username',1,$DATABASE_PASSWORD_FUNCTION('$password'),'$firstName',".
				    "'$lastName','$emailAddress','$phone','$billRate',0,'OUT')");
			dbquery("INSERT INTO $ASSIGNMENTS_TABLE VALUES (1,'$username' )"); // add default project.
			dbquery("INSERT INTO $TASK_ASSIGNMENTS_TABLE VALUES (1,'$username', 1)"); // add default task
		}
		else {
			//get the existing user details
			list($qh, $num) = dbQuery("SELECT first_name, last_name, email_address, phone, bill_rate " .
																"FROM $USER_TABLE WHERE username='$username'");			
			$existingUserDetails = dbResult($qh);
			
			//use existing ones if needs be
			if ($firstName == "")
				$firstName = $existingUserDetails['first_name'];
			if ($lastName == "")
				$lastName = $existingUserDetails['last_name'];
			if ($phone == "")
				$phone = $existingUserDetails['phone'];
			if ($emailAddress == "")			
				$emailAddress = $existingUserDetails['email_address'];
			if ($existingUserDetails['bill_rate'] != 0)
				$billRate = $existingUserDetails['bill_rate'];
		
			//update the users details
			dbquery("UPDATE $USER_TABLE SET first_name='$firstName', last_name='$lastName', ".
								"email_address='$emailAddress', phone='$phone', bill_rate='$billRate' ".
								"WHERE username='$username'");			
		}		
		
		//login succeeded, returning true
		return true;
	}	

	/**
	* returns true if there is a record under that username in the database
	*/
	function userExists($username) {
		require("table_names.inc");			

		//check whether the user exists
		list($qh,$num) = dbQuery("select username from $USER_TABLE where username='$username'");

		//if there is a match
		return ($data = dbResult($qh));
	}
	
	/**
	* returns a string with the reason the login failed
	*/
	function getErrorMessage() {
		if ($this->errorCode != AUTH_FAILED_LDAP_LOGIN)
			return $this->errorText;
			
		if ($this->ldapErrorCode != LDAP_SERVER_ERROR)
			return $this->ldapErrorText;
			
		return $this->ldapServerErrorText;
	}
}

//create the instance so its availiable by just including this file
$authenticationManager = new AuthenticationManager;

?>
Return current item: Timesheet