8000 FEATURE TICKET 2842: Support for multiple LDAP authentications. by wisiemilljungdahl · Pull Request #1 · TestLinkOpenSourceTRMS/testlink-code · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,18 +289,22 @@
/* [User Authentication] */

/**
* Login authentication method:
* Authentication objects
* Login authentication methods:
* 'MD5' => use password stored on db => will be deprecated and DB used.
* 'DB' => Same as MD5 use password stored on db
* 'LDAP' => use password from LDAP Server
* Method specific settings are defined further down with
* $tlCfg->authentication['domain']['<LABEL>'][]
* Multiple authentication objects with same method are allowed, for example:
* 'LDAP1' => array('method' => 'LDAP', ...),
* 'LDAP2' => array('method' => 'LDAP', ...)
*/
$tlCfg->authentication['domain'] = array('DB' => array('description' => 'DB', 'allowPasswordManagement' => true) ,
'LDAP' => array('description' => 'LDAP', 'allowPasswordManagement' => false) );
$tlCfg->authentication['domain'] = array('DB' => array('method' => 'DB', 'description' => 'Local DB', 'allowPasswordManagement' => true),
'LDAP' => array('method' => 'LDAP', 'description' => 'LDAP', 'allowPasswordManagement' => false) );


// $tlCfg->authentication['domain'] = array('DB','LDAP')

/* Default Authentication method */
/** Default Authentication method */
$tlCfg->authentication['method'] = 'DB';

// Applies only if authentication methos is DB.
Expand All @@ -327,16 +331,18 @@
$tlCfg->authentication['SSO_method'] = 'CLIENT_CERTIFICATE';
$tlCfg->authentication['SSO_uid_field'] = 'SSL_CLIENT_S_DN_Email';



/** LDAP authentication credentials */
$tlCfg->authentication['ldap_server'] = 'localhost';
$tlCfg->authentication['ldap_port'] = '389';
$tlCfg->authentication['ldap_version'] = '3'; // could be '2' in some cases
$tlCfg->authentication['ldap_root_dn'] = 'dc=mycompany,dc=com';
$tlCfg->authentication['ldap_bind_dn'] = ''; // Left empty for anonymous LDAP binding
$tlCfg->authentication['ldap_bind_passwd'] = ''; // Left empty for anonymous LDAP binding
$tlCfg->authentication['ldap_tls'] = false; // true -> use tls
$tlCfg->authentication['domain']['LDAP']['ldap_server'] = 'localhost';
$tlCfg->authentication['domain']['LDAP']['ldap_port'] = '389';
$tlCfg->authentication['domain']['LDAP']['ldap_version'] = '3'; // could be '2' in some cases
$tlCfg->authentication['domain']['LDAP']['ldap_root_dn'] = 'dc=mycompany,dc=com';
// Left empty for anonymous LDAP binding
// For dynamic bind account (use the account we are trying to authenticate)
// use keyword '$login', for example:
// ...['ldap_bind_dn'] = '$login@COMPANY.DOMAIN.NAME'
$tlCfg->authentication['domain']['LDAP']['ldap_bind_dn'] = '';
$tlCfg->authentication['domain']['LDAP']['ldap_bind_passwd'] = ''; // Left empty for anonymous LDAP binding
$tlCfg->authentication['domain']['LDAP']['ldap_tls'] = false; // true -> use tls

// Following configuration parameters are used to build
// ldap filter and ldap attributes used by ldap_search()
Expand All @@ -347,8 +353,11 @@
// This can be used to manage situation like explained on post on forum:
// ActiveDirectory + users in AD group
//
$tlCfg->authentication['ldap_organization'] = ''; // e.g. '(organizationname=*Traffic)'
$tlCfg->authentication['ldap_uid_field'] = 'uid'; // Use 'sAMAccountName' for Active Directory
$tlCfg->authentication['domain']['LDAP']['ldap_organization'] = ''; // e.g. '(organizationname=*Traffic)'
$tlCfg->authentication['domain']['LDAP']['ldap_uid_field'] = 'uid'; // Use 'sAMAccountName' for Active Directory
$tlCfg->authentication['domain']['LDAP']['ldap_firstname_field'] = 'givenname';
$tlCfg->authentication['domain']['LDAP']['ldap_surname_field'] = 'sN';
$tlCfg->authentication['domain']['LDAP']['ldap_email_field'] = 'mail';



Expand All @@ -362,12 +371,6 @@
// surname
$tlCfg->authentication['ldap_automatic_user_creation'] = false;

// Configure following fields in custom_config.inc.php according your configuration
$tlCfg->authentication['ldap_email_field'] = 'mail';
$tlCfg->authentication['ldap_firstname_field'] = 'givenname';
$tlCfg->authentication['ldap_surname_field'] = 'sn';




/** Enable/disable Users to create accounts on login page */
Expand Down
36 changes: 27 additions & 9 deletions lib/functions/configCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,37 @@ function getSecurityNotes(&$db)
}

$authCfg = config_get('authentication');
if( 'LDAP' == $authCfg['method'] )
$use_db = false;
$use_ldap = false;

foreach ($authCfg['domain'] AS $authDomain => $domainSettings)
{
if( !checkForLDAPExtension() )
// Make sure to be backward compatible with domain key == method
$authMethod = $authDomain;
if (is_array($domainSettings) && !empty($domainSettings['method']))
{
$securityNotes[] = lang_get("ldap_extension_not_loaded");
}
}
else
{
if( checkForAdminDefaultPwd($db) )
$authMethod = $domainSettings['method'];
}

if ('DB' == $authMethod ||
'MD5' == $authMethod)
{
$securityNotes[] = lang_get("sec_note_admin_default_pwd");
$use_db = true;
}
if ('LDAP' == $authMethod)
{
$use_ldap = true;
}
}

if (($use_ldap) && (!checkForLDAPExtension()))
{
$securityNotes[] = lang_get("ldap_extension_not_loaded");
}

if (($use_db) && (checkForAdminDefaultPwd($db)))
{
$securityNotes[] = lang_get("sec_note_admin_default_pwd");
}


Expand Down
102 changes: 73 additions & 29 deletions lib/functions/doAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,57 @@ function doAuthorize(&$db,$login,$pwd,$options=null)
$authCfg = config_get('authentication');
if( $authCfg['ldap_automatic_user_creation'] )
{
$user->authentication = 'LDAP'; // force for auth_does_password_match
$check = auth_does_password_match($user,$pwd);

if( $check->status_ok )
// Loop through each domain with LDAP method
foreach ($authCfg['domain'] AS $authDomain => $domainSettings)
{
$user = new tlUser();
$user->login = $login;
$user->authentication = 'LDAP';
$user->isActive = true;
$user->setPassword($pwd); // write password on DB anyway
// Make sure to be backward compatible with domain key == method
$authMethod = $authDomain;
if (is_array($domainSettings) && !empty($domainSettings['method']))
{
$authMethod = $domainSettings['method'];
}

if ('LDAP' == $authMethod)
{
$user->authentication = $authDomain; // force for auth_does_password_match
$check = auth_does_password_match($user,$pwd);

if( $check->status_ok )
{
$user = new tlUser();
$user->login = $login;
$user->authentication = $authDomain;
$user->isActive = true;
$user->setPassword($pwd); // write password on DB anyway - really!?

$user->emailAddress = ldap_get_field_from_username($user->login,strtolower($authCfg['ldap_email_field']));
$user->firstName = ldap_get_field_from_username($user->login,strtolower($authCfg['ldap_firstname_field']));
$user->lastName = ldap_get_field_from_username($user->login,strtolower($authCfg['ldap_surname_field']));
$authDetails = array();
if(isset($domainSettings['ldap_server']))
{
// If ldap_server found, assume new config structure
$authDetails = $domainSettings;
}
else
{
// Fallback to old config structure
$authDetails = $authCfg;
}

$user->firstName = (is_null($user->firstName) || strlen($user->firstName) == 0) ? $login : $user->firstName;
$user->lastName = (is_null($user->lastName) || strlen($user->lastName) == 0) ? $login : $user->lastName;
$user->emailAddress = ldap_get_field_from_username($authDetails,$user->login,strtolower($authDetails['ldap_email_field']));
$user->firstName = ldap_get_field_from_username($authDetails,$user->login,strtolower($authDetails['ldap_firstname_field']));
$user->lastName = ldap_get_field_from_username($authDetails,$user->login,strtolower($authDetails['ldap_surname_field']));

$user->firstName = (is_null($user->firstName) || strlen($user->firstName) == 0) ? $login : $user->firstName;
$user->lastName = (is_null($user->lastName) || strlen($user->lastName) == 0) ? $login : $user->lastName;

$doLogin = ($user->writeToDB($db) == tl::OK);
}
}
}

$doLogin = ($user->writeToDB($db) == tl::OK);

break;
}
}
}
}
}
}

if( $doLogin )
Expand Down Expand Up @@ -206,20 +234,22 @@ function auth_does_password_match(&$userObj,$cleartext_password)
$ret = new stdClass();
$ret->status_ok = false;
$ret->msg = sprintf(lang_get('unknown_authentication_method'),$authCfg['method']);

$authMethod = $userObj->authentication;
switch($userObj->authentication)

$authDomain = $userObj->authentication;
// Verify that user selected auth is valid, else use default
if(empty($authDomain) || !array_key_exists($authDomain, $authCfg['domain']))
{
case 'DB':
case 'LDAP':
break;
$authDomain = $authCfg['method'];
}

default:
$authMethod = $authCfg['method'];
break;
// Get authentication method
// Make sure to be backward compatible (key defined method)
$authMethod = $authDomain;
if(is_array($authCfg['domain'][$authDomain]) && !empty($authCfg['domain'][$authDomain]['method']))
{
$authMethod = $authCfg['domain'][$authDomain]['method'];
}

// switch($authCfg['method'])
switch($authMethod)
{
case 'LDAP':
Expand All @@ -229,8 +259,22 @@ function auth_does_password_match(&$userObj,$cleartext_password)
$msg[ERROR_LDAP_USER_NOT_FOUND] = lang_get('error_ldap_user_not_found');
$msg[ERROR_LDAP_BIND_FAILED] = lang_get('error_ldap_bind_failed');
$msg[ERROR_LDAP_START_TLS_FAILED] = lang_get('error_ldap_start_tls_failed');

// Resolv LDAP config
// Make sure to be backward compatible with old config files (with "global" LDAP settings)
$ldapDetails = array();
if (isset($authCfg['domain'][$authDomain]['ldap_server']))
{
// If ldap_server found, assume new config structure
$ldapDetails = $authCfg['domain'][$authDomain];
}
else
{
// Fallback to old config structure
$ldapDetails = $authCfg;
}

$xx = ldap_authenticate($userObj->login, $cleartext_password);
$xx = ldap_authenticate($ldapDetails, $userObj->login, $cleartext_password);
$ret->status_ok = $xx->status_ok;
$ret->msg = $xx->status_ok ? 'ok' : $msg[$xx->status_code];
break;
Expand Down
34 changes: 25 additions & 9 deletions lib/functions/ldap_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@
*/

// Connect and bind to the LDAP directory
function ldap_connect_bind( $p_binddn = '', $p_password = '', $context = '')
function ldap_connect_bind( $authCfg, $p_binddn = '', $p_password = '', $context = '')
{
$ret = new stdClass();
$ret->status = 0;
$ret->handler = null;
$ret->info = 'LDAP CONNECT OK';

$authCfg = config_get('authentication');
$t_message = "Attempting connection to LDAP ";
$t_ldap_uri = parse_url($authCfg['ldap_server']);
if(count( $t_ldap_uri ) > 1)
Expand Down Expand Up @@ -135,7 +134,7 @@ function ldap_connect_bind( $p_binddn = '', $p_password = '', $context = '')< 5276 /td>

// ----------------------------------------------------------------------------
// Attempt to authenticate the user against the LDAP directory
function ldap_authenticate( $p_login_name, $p_password )
function ldap_authenticate( $authCfg, $p_login_name, $p_password )
{
# if password is empty and ldap allows anonymous login, then
# the user will be able to login, hence, we need to check
Expand All @@ -150,8 +149,6 @@ function ldap_authenticate( $p_login_name, $p_password )
$t_authenticated->status_code = null;
$t_authenticated->status_verbose = '';

$authCfg = config_get('authentication');

$t_ldap_organization = $authCfg['ldap_organization'];
$t_ldap_root_dn = $authCfg['ldap_root_dn'];
$t_ldap_uid_field = $authCfg['ldap_uid_field']; // 'uid' by default
Expand All @@ -161,7 +158,18 @@ function ldap_authenticate( $p_login_name, $p_password )
$t_search_filter = "(&$t_ldap_organization($t_ldap_uid_field=$t_username))";
// $t_search_attrs = array( $t_ldap_uid_field, 'dn', 'mail','displayName');
$t_search_attrs = array( $t_ldap_uid_field, 'dn');
$t_connect = ldap_connect_bind();

// Check for special "bind with user credentials config"
// e.g. $authCfg['ldap_bind_dn'] contains keyword $login
$bind_login = '';
$bind_pwd = '';
if(strpos($authCfg['ldap_bind_dn'], '$login') !== false)
{
$bind_login = str_replace('$login', $p_login_name, $authCfg['ldap_bind_dn']);
$bind_pwd = $p_password;
}

$t_connect = ldap_connect_bind($authCfg, $bind_login, $bind_pwd);

if( $t_connect->status == 0 )
{
Expand Down Expand Up @@ -249,16 +257,24 @@ function ldap_escape_string( $p_string )
* @param string $p_field The LDAP field name.
* @return string The field value or null if not found.
*/
function ldap_get_field_from_username( $p_username, $p_field ) {
function ldap_get_field_from_username( $authCfg, $p_username, $p_field ) {

$authCfg = config_get('authentication');
$t_ldap_organization = $authCfg['ldap_organization'];
$t_ldap_root_dn = $authCfg['ldap_root_dn'];
$t_ldap_uid_field = $authCfg['ldap_uid_field']; // 'uid' by default

$c_username = ldap_escape_string( $p_username );

$t_connect = @ldap_connect_bind();
// Check for special "bind with user credentials config"
// e.g. $authCfg['ldap_bind_dn'] contains keyword $login
$bind_login = '';
$bind_pwd = '';
if(strpos($authCfg['ldap_bind_dn'], '$login') !== false)
{
$bind_login = str_replace('$login', $p_login_name, $authCfg['ldap_bind_dn']);
$bind_pwd = $p_password;
}
$t_connect = @ldap_connect_bind($authCfg, $bind_login, $bind_pwd);
if ( $t_connect === false ) {
return null;
}
Expand Down
9 changes: 6 additions & 3 deletions lib/functions/tlUser.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ class tlUser extends tlDBObject

/** configuration options */
protected $usernameFormat;
protected $loginMethod;
protected $maxLoginLength;

/** error codes */
Expand Down Expand Up @@ -150,7 +149,7 @@ function __construct($dbID = null)
$this->usernameFormat = config_get('username_format');
$this->loginRegExp = config_get('validation_cfg')->user_login_valid_regex;
$this->maxLoginLength = 30;
$this->loginMethod = $authCfg['method'];
$this->authentication = $authCfg['method'];

$this->globalRoleID = config_get('default_roleid');
$this->locale = config_get('default_language');
Expand Down Expand Up @@ -203,7 +202,11 @@ static public function isPasswordMgtExternal($method2check=null)
{
$authCfg = config_get('authentication');
$target = $authCfg['method'];
}
}
if(is_array($authCfg['domain'][$target]) && !empty($authCfg['domain'][$target]['method']))
{
$target = $authCfg['domain'][$target]['method'];
}
switch($target)
{
case 'LDAP':
Expand Down
Loading
0