%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/ldap/classes/general/ |
| Current File : /home/bitrix/www/bitrix/modules/ldap/classes/general/ldap.php |
<?php
/**
* Bitrix Framework
* @package bitrix
* @subpackage ldap
* @copyright 2001-2016 Bitrix
*/
IncludeModuleLangFile(__FILE__);
class CLDAP
{
var $arFields, $arGroupList = false;
var $conn;
protected static $PHOTO_ATTRIBS = array("thumbnailPhoto", "jpegPhoto");
protected $arGroupMaps;
protected $groupsLists = array();
const CONNECTION_TYPE_SIMPLE = 0;
const CONNECTION_TYPE_SSL = 1;
const CONNECTION_TYPE_TLS = 2;
protected $isTlsStarted = false;
function Connect($arFields = Array())
{
global $APPLICATION;
if(!isset($this) || !is_object($this))
{
$ldap = new CLDAP();
$ldap->arFields = $arFields;
if($ldap->Connect())
return $ldap;
return false;
}
if($this->conn = @ldap_connect($this->arFields["SERVER"], $this->arFields['PORT']))
{
@ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3);
@ldap_set_option($this->conn, LDAP_OPT_REFERRALS, 0);
@ldap_set_option($this->conn, LDAP_OPT_SIZELIMIT, COption::GetOptionInt("ldap", "group_limit", 0));
@ldap_set_option($this->conn, LDAP_OPT_TIMELIMIT, 100);
@ldap_set_option($this->conn, LDAP_OPT_TIMEOUT, 5);
@ldap_set_option($this->conn, LDAP_OPT_NETWORK_TIMEOUT, 5);
$login = isset($this->arFields["~ADMIN_LOGIN"]) ? $this->arFields["~ADMIN_LOGIN"] : $this->arFields["ADMIN_LOGIN"];
$pass = isset($this->arFields["~ADMIN_PASSWORD"]) ? $this->arFields["~ADMIN_PASSWORD"] : $this->arFields["ADMIN_PASSWORD"];
return $this->Bind($login, $pass);
}
else
{
$APPLICATION->ThrowException('ldap_connect() error. '.$this->getLastErrorDescription());
}
return false;
}
function BindAdmin()
{
if(strlen($this->arFields["ADMIN_LOGIN"]) <= 0)
return false;
return $this->Bind(
(isset($this->arFields["~ADMIN_LOGIN"])?$this->arFields["~ADMIN_LOGIN"]:$this->arFields["ADMIN_LOGIN"]),
(isset($this->arFields["~ADMIN_PASSWORD"])?$this->arFields["~ADMIN_PASSWORD"]:$this->arFields["ADMIN_PASSWORD"])
);
}
function Bind($login, $password)
{
if(!$this->conn)
return false;
global $APPLICATION;
if($this->arFields["CONVERT_UTF8"] == "Y")
{
$login = $APPLICATION->ConvertCharset($login, SITE_CHARSET, "utf-8");
$password = $APPLICATION->ConvertCharset($password, SITE_CHARSET, "utf-8");
}
if(strpos($password, "\0") !== false || strlen($password) <= 0)
return false;
if(intval($this->arFields["CONNECTION_TYPE"]) == CLDAP::CONNECTION_TYPE_TLS)
if(!$this->startTls())
return false;
if(!@ldap_bind($this->conn, $login, $password))
{
$APPLICATION->ThrowException('ldap_bind() error. '.$this->getLastErrorDescription());
return false;
}
return true;
}
protected function startTls()
{
global $APPLICATION;
if($this->isTlsStarted)
return true;
if(!@ldap_start_tls($this->conn))
{
$APPLICATION->ThrowException('ldap_start_tls() error. '.$this->getLastErrorDescription());
return false;
}
$this->isTlsStarted = true;
return true;
}
function Disconnect()
{
ldap_close($this->conn);
}
function RootDSE()
{
$values = $this->_RootDSE('namingcontexts');
if ($values == false)
$values = $this->_RootDSE('namingContexts');
return $this->WorkAttr($values);
}
function _RootDSE($filtr)
{
$sr = ldap_read($this->conn, '', 'objectClass=*', Array($filtr), 0);
//$sr = ldap_read($this->conn, '', 'objectClass=*');
$entry = ldap_first_entry($this->conn, $sr);
$attributes = ldap_get_attributes($this->conn, $entry);
$values = false;
if ($attributes['count'] > 0)
$values = @ldap_get_values_len($this->conn, $entry, $filtr);
return $values;
}
function WorkAttr($values)
{
global $APPLICATION;
if(is_array($values) && $values['count']==1)
{
if($this->arFields["CONVERT_UTF8"]=="Y" && strtolower(SITE_CHARSET) != "utf-8")
return $APPLICATION->ConvertCharset($values[0], "utf-8", SITE_CHARSET);
return $values[0];
}
unset($values['count']);
if($this->arFields["CONVERT_UTF8"]=="Y" && strtolower(SITE_CHARSET) != "utf-8")
foreach($values as $key=>$val)
$values[$key] = $APPLICATION->ConvertCharset($val, "utf-8", SITE_CHARSET);
return $values;
}
function QueryArray($str = '(ObjectClass=*)', $fields = false)
{
global $APPLICATION;
if(strlen($this->arFields['BASE_DN'])<=0)
return false;
$arBaseDNs = explode(";", $this->arFields['BASE_DN']);
$info = false;
$i=0;
if($this->arFields["CONVERT_UTF8"] == "Y")
$str = $APPLICATION->ConvertCharset($str, SITE_CHARSET, "utf-8");
foreach($arBaseDNs as $BaseDN)
{
global $APPLICATION;
$BaseDN = trim($BaseDN);
if($BaseDN == "")
continue;
if($this->arFields["CONVERT_UTF8"]=="Y")
$BaseDN = $APPLICATION->ConvertCharset($BaseDN, SITE_CHARSET, "utf-8");
$defaultMaxPageSizeAD = 1000;
$pageSize = isset($this->arFields['MAX_PAGE_SIZE']) && intval($this->arFields['MAX_PAGE_SIZE'] > 0) ? intval($this->arFields['MAX_PAGE_SIZE']) : $defaultMaxPageSizeAD;
$cookie = '';
do
{
if(CLdapUtil::isLdapPaginationAviable())
ldap_control_paged_result($this->conn, $pageSize, false, $cookie);
if($fields === false)
$sr = @ldap_search($this->conn, $BaseDN, $str);
else
$sr = @ldap_search($this->conn, $BaseDN, $str, $fields);
if($sr)
{
$entry = ldap_first_entry($this->conn, $sr);
if($entry)
{
if(!is_array($info))
{
$info = Array();
$i=0;
}
do
{
$attributes = ldap_get_attributes($this->conn, $entry);
for($j=0; $j<$attributes['count']; $j++)
{
$values = @ldap_get_values_len($this->conn, $entry, $attributes[$j]);
if($values === false)
continue;
$bPhotoAttr = in_array($attributes[$j], self::$PHOTO_ATTRIBS);
$info[$i][strtolower($attributes[$j])] = $bPhotoAttr ? $values : $this->WorkAttr($values);
}
if(!is_set($info[$i], 'dn'))
{
if($this->arFields["CONVERT_UTF8"]=="Y")
$info[$i]['dn'] = $APPLICATION->ConvertCharset(ldap_get_dn($this->conn, $entry), "utf-8", SITE_CHARSET);
else
$info[$i]['dn'] = ldap_get_dn($this->conn, $entry);
}
$i++;
}
while($entry = ldap_next_entry($this->conn, $entry));
}
if(CLdapUtil::isLdapPaginationAviable())
@ldap_control_paged_result_response($this->conn, $sr, $cookie);
}
elseif($sr === false)
{
$APPLICATION->ThrowException("LDAP_SEARCH_ERROR");
}
} while($cookie !== null && $cookie != '');
}
return $info;
}
function Query($str = '(ObjectClass=*)', $fields = false)
{
$info = $this->QueryArray($str, $fields);
$result = new CDBResult;
$result->InitFromArray($info);
return $result;
}
protected function setFieldAsAttr(array $attrArray, $fieldName)
{
$field = isset($this->arFields["~".$fieldName]) ? $this->arFields["~".$fieldName] : $this->arFields[$fieldName];
$field = strtolower($field);
if(!in_array($field, $attrArray))
$attrArray[] = $field;
return $attrArray;
}
// query for group list from AD - server
function GetGroupListArray($query = '')
{
$group_filter = $this->arFields['GROUP_FILTER'];
if(strlen(trim($group_filter))>0 && substr(trim($group_filter), 0, 1)!='(')
$group_filter = '('.trim($group_filter).')';
$query = '(&'.$group_filter.$query.')';
if (!array_key_exists($query, $this->groupsLists))
{
$this->BindAdmin();
$arGroupAttr = array(
"name", "cn", "gidNumber", "description", "memberof",
"primarygrouptoken", "primarygroupid", "samaccountname",
"distinguishedname"
);
foreach(array("GROUP_ID_ATTR", "GROUP_NAME_ATTR", "GROUP_MEMBERS_ATTR") as $fieldName)
$arGroupAttr = $this->setFieldAsAttr($arGroupAttr, $fieldName);
if ($this->arFields['USER_GROUP_ACCESSORY'] == 'Y')
$arGroupAttr = $this->setFieldAsAttr($arGroupAttr, "USER_GROUP_ATTR");
$arGroupsTmp = $this->QueryArray($query, $arGroupAttr);
if (!$arGroupsTmp)
return false;
$arGroups = array();
$group_id_attr = strtolower($this->arFields['GROUP_ID_ATTR']);
if(is_set($this->arFields, 'GROUP_NAME_ATTR'))
$group_name_attr = strtolower($this->arFields['GROUP_NAME_ATTR']);
else
$group_name_attr = false;
foreach ($arGroupsTmp as $grp)
{
$grp['ID'] = $grp[$group_id_attr];
if ($group_name_attr && is_set($grp, $group_name_attr))
$grp['NAME'] = $grp[$group_name_attr];
$arGroups[$grp['ID']] = $grp;
}
$this->groupsLists[$query] = $arGroups;
}
return $this->groupsLists[$query];
}
function GetGroupList($query = '')
{
$arGroups = $this->GetGroupListArray($query);
$result = new CDBResult();
$result->InitFromArray($arGroups);
return $result;
}
function OnUserLogin($arArgs)
{
global $APPLICATION;
if(!function_exists("ldap_connect"))
return false;
$LOGIN = $arArgs["LOGIN"];
$PASSWORD = $arArgs["PASSWORD"];
if(strlen($LOGIN)<=0 || strlen($PASSWORD)<=0)
return false;
$arFilter = Array("ACTIVE"=>"Y");
$p = strpos($LOGIN, "\\");
if( $p===false && COption::GetOptionString("ldap", "ntlm_auth_without_prefix", "Y") != "Y")
{
return false;
}
elseif( $p > 0 )
{
$arFilter["CODE"] = substr($LOGIN, 0, $p);
$LOGIN = substr($LOGIN, $p+1);
}
$arParams = Array(
"LOGIN" => &$LOGIN,
"PASSWORD" => &$PASSWORD,
"LDAP_FILTER" => &$arFilter,
);
$APPLICATION->ResetException();
foreach(GetModuleEvents("ldap", "OnBeforeUserLogin", true) as $arEvent)
{
// TODO check whether wrapping of &$arParams into another array is reasonable as part of migration from ExecuteModuleEvent to ExecuteModuleEventEx
if(ExecuteModuleEventEx($arEvent, array(&$arParams))===false)
{
if($err = $APPLICATION->GetException())
{
$result_message = Array("MESSAGE"=>$err->GetString()."<br>", "TYPE"=>"ERROR");
}
else
{
$APPLICATION->ThrowException("Unknown error");
$result_message = Array("MESSAGE"=>"Unknown error"."<br>", "TYPE"=>"ERROR");
}
return false;
}
}
$db_ldap_serv = CLdapServer::GetList(Array(), $arFilter);
while($xLDAP = $db_ldap_serv->GetNextServer())
{
if($xLDAP->Connect())
{
// user AD parameters are queried here, inside FindUser function
if(!$arLdapUser = $xLDAP->FindUser($LOGIN, $PASSWORD))
{
if(isset($arArgs["OTP"]) && strlen($arArgs["OTP"]) > 0)
if(substr($PASSWORD, -6) == $arArgs["OTP"])
$arLdapUser = $xLDAP->FindUser($LOGIN, substr($PASSWORD, 0, -6)); //It can be with otp
}
if($arLdapUser)
{
$ID = $xLDAP->SetUser($arLdapUser, (COption::GetOptionString("ldap", "add_user_when_auth", "Y")=="Y"));
if($ID > 0)
{
$arArgs["STORE_PASSWORD"] = "N";
$xLDAP->Disconnect();
return $ID;
}
}
$xLDAP->Disconnect();
}
}
return false;
}
// this function is called on user logon (either normal or ntlm) to find user in ldap
function FindUser($LOGIN, $PASSWORD = false)
{
$login_field = $LOGIN;
$password_field = $PASSWORD;
$this->BindAdmin();
$user_filter = "(&".$this->arFields["~USER_FILTER"]."(".$this->arFields["~USER_ID_ATTR"]."=".$this->specialchars($login_field)."))";
$dbLdapUsers = $this->Query($user_filter);
if (!$dbLdapUsers)
return false;
if($arLdapUser = $dbLdapUsers->Fetch())
{
if($PASSWORD !== false) // also check auth
{
$user_dn = $arLdapUser['dn'];
if (!$this->Bind($user_dn, $password_field))
return false;
}
return $this->GetUserFields($arLdapUser);
}
return false;
}
/**
* Returns value of ldap user field mapped to bitrix field.
* @param string $fieldName Name of user field in Bitrix system.
* @param array $arLdapUser User params received from ldap.
* @return mixed.
*/
function getLdapValueByBitrixFieldName($fieldName, $arLdapUser)
{
global $USER_FIELD_MANAGER;
if(!isset($this->arFields["FIELD_MAP"][$fieldName]))
return false;
$attr = $this->arFields["FIELD_MAP"][$fieldName];
$arRes = $USER_FIELD_MANAGER->GetUserFields("USER", 0, LANGUAGE_ID);
$result = false;
if(is_array($arRes[$fieldName]))
{
if($arRes[$fieldName]["MULTIPLE"]=="Y")
{
if (is_array($arLdapUser[strtolower($attr)]))
$result = array_values($arLdapUser[strtolower($attr)]);
else
$result = array($arLdapUser[strtolower($attr)]);
}
else if (!empty($arLdapUser[strtolower($attr)]))
$result = $arLdapUser[strtolower($attr)];
else if (!empty($arRes[$fieldName]['SETTINGS']['DEFAULT_VALUE']))
{
if (is_array($arRes[$fieldName]['SETTINGS']['DEFAULT_VALUE']))
{
if (!empty($arRes[$fieldName]['SETTINGS']['DEFAULT_VALUE']['VALUE']))
$result = $arRes[$fieldName]['SETTINGS']['DEFAULT_VALUE']['VALUE'];
}
else
$result = $arRes[$fieldName]['SETTINGS']['DEFAULT_VALUE'];
}
}
elseif(preg_match("/(.*)&([0-9]+)/", $attr, $arMatch))
{
if(intval($arLdapUser[strtolower($arMatch[1])]) & intval($arMatch[2]))
$result = "N";
else
$result = "Y";
}
elseif ($fieldName == "PERSONAL_PHOTO")
{
if($arLdapUser[strtolower($attr)] == "")
return false;
$fExt = CLdapUtil::GetImgTypeBySignature($arLdapUser[strtolower($attr)][0]);
if(!$fExt)
return false;
$tmpDir = CTempFile::GetDirectoryName();
CheckDirPath($tmpDir);
$fname = "ad_".rand().".".$fExt;
if(!file_put_contents($tmpDir.$fname,$arLdapUser[strtolower($attr)][0]))
return false;
$result = array(
"name" => $fname,
"type" => CFile::GetContentType($tmpDir.$fname),
"tmp_name" => $tmpDir.$fname
);
}
else
$result = $arLdapUser[strtolower($attr)];
if(is_null($result))
$result = false;
return $result;
}
public static function OnFindExternalUser($login)
{
if(strlen($login) <= 0)
return 0;
$filter = array("ACTIVE" => "Y");
$p = strpos($login, "\\");
if($p === false && COption::GetOptionString("ldap", "ntlm_auth_without_prefix", "Y") != "Y")
{
return 0;
}
elseif( $p > 0 )
{
$filter["CODE"] = substr($login, 0, $p);
$login = substr($login, $p+1);
}
$dbServ = CLdapServer::GetList(array(), $filter);
while($serv = $dbServ->GetNextServer())
{
if($serv->Connect())
{
if($arLdapUser = $serv->FindUser($login))
{
$id = $serv->SetUser($arLdapUser, (COption::GetOptionString("ldap", "add_user_when_auth", "Y") == "Y"));
if($id > 0)
{
$serv->Disconnect();
return $id;
}
}
$serv->Disconnect();
}
}
return 0;
}
// converts LDAP values to those suitable for user fields
function GetUserFields($arLdapUser, &$departmentCache=FALSE)
{
global $APPLICATION;
$arFields = array(
'DN' => $arLdapUser['dn'],
'LOGIN' => $arLdapUser[strtolower($this->arFields['~USER_ID_ATTR'])],
'EXTERNAL_AUTH_ID' => 'LDAP#'.$this->arFields['ID'],
'LDAP_GROUPS' => $arLdapUser[strtolower($this->arFields['~USER_GROUP_ATTR'])],
);
// for each field, do the conversion
foreach($this->arFields["FIELD_MAP"] as $userField=>$attr)
$arFields[$userField] = $this->getLdapValueByBitrixFieldName($userField, $arLdapUser);
$APPLICATION->ResetException();
$db_events = GetModuleEvents("ldap", "OnLdapUserFields");
while($arEvent = $db_events->Fetch())
{
$arParams = array(array(&$arFields, &$arLdapUser));
if(ExecuteModuleEventEx($arEvent, $arParams)===false)
{
if(!($err = $APPLICATION->GetException()))
$APPLICATION->ThrowException("Unknown error");
return false;
}
$arFields = $arParams[0][0];
}
// set a department field, if needed
if (empty($arFields['UF_DEPARTMENT']) && isModuleInstalled('intranet')
&& $this->arFields['IMPORT_STRUCT'] && $this->arFields['IMPORT_STRUCT']=='Y')
{
//$arLdapUser[$this->arFields['USER_DN_ATTR']]
$username = $arLdapUser[$this->arFields['USER_ID_ATTR']];
if ($arDepartment = $this->GetDepartmentIdForADUser($arLdapUser[$this->arFields['USER_DEPARTMENT_ATTR']],$arLdapUser[$this->arFields['USER_MANAGER_ATTR']],$username,$departmentCache))
{
// fill in cache. it is done outside the function because it has many exit points
if ($departmentCache)
$departmentCache[$username] = $arDepartment;
// this is not final assignment
// $arFields['UF_DEPARTMENT'] sould contain array of department ids
// but somehow we have to return an information whether this user is a department head
// so we'll save this data here temporarily
$arFields['UF_DEPARTMENT'] = $arDepartment;
}
else
$arFields['UF_DEPARTMENT'] = array();
// at this point $arFields['UF_DEPARTMENT'] should be set to some value, even an empty array is ok
}
if (!is_array($arFields['LDAP_GROUPS']))
$arFields['LDAP_GROUPS'] = (!empty($arFields['LDAP_GROUPS']) ? array($arFields['LDAP_GROUPS']) : array());
$primarygroupid_name_attr = 'primarygroupid';
$primarygrouptoken_name_attr = 'primarygrouptoken';
$groupMemberAttr = null;
$userIdAttr = null;
if ($this->arFields['USER_GROUP_ACCESSORY'] == 'Y')
{
$primarygroupid_name_attr = strtolower($this->arFields['GROUP_ID_ATTR']);
$primarygrouptoken_name_attr = strtolower($this->arFields['USER_GROUP_ATTR']);
$userIdAttr = strtolower($this->arFields['USER_ID_ATTR']);
$groupMemberAttr = strtolower($this->arFields['GROUP_MEMBERS_ATTR']);
}
$arAllGroups = $this->GetGroupListArray();
if (!is_array($arAllGroups) || count($arAllGroups) <= 0)
return $arFields;
$arGroup = reset($arAllGroups);
do
{
if(in_array($arGroup['ID'], $arFields['LDAP_GROUPS']))
continue;
if (
(is_set($arLdapUser, $primarygroupid_name_attr)
&& $arGroup[$primarygrouptoken_name_attr] == $arLdapUser[$primarygroupid_name_attr]
)
||
($this->arFields['USER_GROUP_ACCESSORY'] == 'Y'
&& is_set($arGroup, $groupMemberAttr)
&& (
(is_array($arGroup[$groupMemberAttr])
&& in_array($arLdapUser[$userIdAttr], $arGroup[$groupMemberAttr])
)
||
$arLdapUser[$userIdAttr] == $arGroup[$groupMemberAttr]
)
)
)
{
$arFields['LDAP_GROUPS'][] = $arGroup['ID'];
if ($this->arFields['USER_GROUP_ACCESSORY'] == 'N')
break;
}
}
while ($arGroup = next($arAllGroups));
return $arFields;
}
// Gets department ID for AD user. If department doesn't exist, creates a new one. Returns FALSE if there should be no department set.
// returns array:
// 'ID' - department id
// 'IS_HEAD' - true if this user is head of the department, false if not
function GetDepartmentIdForADUser($department, $managerDN, $username, &$cache=FALSE, $iblockId = FALSE, $names = FALSE)
{
global $USER_FIELD_MANAGER;
// check for loops in manager structure, if loop is found - quit
// should be done before cache lookup
if ($names && isset($names[$username]))
return false;
// if department id for this user is already stored in cache
if ($cache)
{
$departmentCached = $cache[$username];
// if user was not set as head earlier, then do not get his id from cache
if ($departmentCached)
return $departmentCached;
}
// if it is a first call in recursive chain
if (!$iblockId)
{
// check module inclusions
if (!IsModuleInstalled('intranet') || !CModule::IncludeModule('iblock'))
return false;
// get structure's iblock id
$iblockId=COption::GetOptionInt("intranet", "iblock_structure", false, false);
if (!$iblockId)
return false;
$names = array();
}
// save current username as already visited
$names[$username] = true;
$arManagerDep = null;
$mgrDepartment = null;
// if there's a manager - query it
if ($managerDN)
{
preg_match('/^((CN|uid)=.*?)(\,){1}([^\,])*(=){1}/i', $managerDN, $matches); //Extract "CN=User Name" from full name
$user = isset($matches[1]) ? str_replace('\\', '',$matches[1]) : "";
$userArr = $this->GetUserArray($user);
if (count($userArr)>0)
{
// contents of userArr are already in local encoding, no need for conversion here
$mgrDepartment = $userArr[0][$this->arFields['USER_DEPARTMENT_ATTR']];
if ($mgrDepartment && trim($mgrDepartment)!='')
{
// if manager's department name is set - then get it's id
$mgrManagerDN = $userArr[0][$this->arFields['USER_MANAGER_ATTR']];
$mgrUserName = $userArr[0][$this->arFields['USER_ID_ATTR']];
$arManagerDep = $this->GetDepartmentIdForADUser($mgrDepartment, $mgrManagerDN, $mgrUserName, $cache, $iblockId, $names);
// fill in cache
if ($cache && $arManagerDep)
$cache[$mgrUserName] = $arManagerDep;
}
}
}
// prepare result and create department (if needed)
$arResult = array('IS_HEAD'=>true); // by default, thinking of user as a head of the department
if ($arManagerDep)
{
// if got manager's data correctly
if ($department && trim($department)!='' && ($mgrDepartment!=$department))
{
// if our department is set && differs from manager's, set manager's as parent
$parentSectionId = $arManagerDep['ID'];
}
else
{
// - if user has no department, but somehow have manager - then he is assumed to be in manager's department
// - if user has same department name as manager - then he is not head
// here we can return manager's department id immediately
$arResult = $arManagerDep;
$arResult['IS_HEAD'] = false;
return $arResult;
}
}
else
{
// if there's no manager's data
if ($department && trim($department)!='')
{
$parentSectionId = $this->arFields['ROOT_DEPARTMENT'];
}
else
{
// if have no manager's department and no own department:
// - use default as our department and root as parent section if default is set
// - or just root if default has empty value
// - or return false, if setting of default department is turned off
if ($this->arFields['STRUCT_HAVE_DEFAULT'] && $this->arFields['STRUCT_HAVE_DEFAULT'] == "Y")
{
// if can use default department
$department = $this->arFields['DEFAULT_DEPARTMENT_NAME'];
if ($department && trim($department)!='')
{
// if department is not empty
$parentSectionId = $this->arFields['ROOT_DEPARTMENT'];
}
else
{
// if it is empty - return parent
return array('ID' => $this->arFields['ROOT_DEPARTMENT']);
}
}
else
{
// if have no department in AD and no default - then do not set a department
return false;
}
}
}
// 3. if there's no department set for this user, this means there was no default department name (which substituted in *) - then there's no need to set department id for this user at all
if (!$department || trim($department)=='')
return false;
// 4. detect this user's department ID, using parent id and department name string, which we certainly have now (these 2 parameters are required to get an ID)
// see if this department already exists
$bs = new CIBlockSection();
$dbExistingSections = GetIBlockSectionList(
$iblockId,
($parentSectionId >= 0 ? $parentSectionId : false),
$arOrder = Array("left_margin" => "asc"),
$cnt = 0,
$arFilter = Array('NAME' => $department)
);
$departmentId = false;
if($arItem = $dbExistingSections->GetNext())
$departmentId = $arItem['ID'];
if (!$departmentId)
{
//create new department
$arNewSectFields = Array(
"ACTIVE" => "Y",
"IBLOCK_ID" => $iblockId,
"NAME" => $department
);
if ($parentSectionId>=0)
$arNewSectFields["IBLOCK_SECTION_ID"] = $parentSectionId;
// and get it's Id
$departmentId = $bs->Add($arNewSectFields);
}
$arElement = $USER_FIELD_MANAGER->GetUserFields(
'IBLOCK_'.$iblockId.'_SECTION',
$departmentId
);
// if the head of the department is already set, do not change it
if (!empty($arElement['UF_HEAD']['VALUE']))
$arResult['IS_HEAD'] = false;
$arResult['ID'] = $departmentId;
return $arResult;
}
// get user list (with attributes) from AD server
function GetUserList($arFilter = Array())
{
$query = '';
foreach($arFilter as $key=>$value)
{
$key = strtoupper($key);
switch($key)
{
case 'GROUP_ID':
//"SELECT ".
// "FROM "
case 'GROUP_DN':
$temp = '';
$temp_cnt = 0;
if(!is_array($value))
$value = array($value);
foreach($value as $group)
{
if(strlen($group)<=0)
continue;
$temp_cnt++;
$temp .= '('.$this->arFields['USER_GROUP_ATTR'].'='.$this->specialchars($group).')';
}
$query .= '(|'.$temp.')';
break;
}
}
$user_filter = $this->arFields['USER_FILTER'];
if(strlen(trim($user_filter))>0 && substr(trim($user_filter), 0, 1)!='(')
$user_filter = '('.trim($user_filter).')';
$query = '(&'.$user_filter.$query.')';
$arResult = $this->Query($query);
return $arResult;
}
function GetUserArray($cn)
{
$user_filter = $this->arFields['USER_FILTER'];
if(strlen(trim($user_filter))>0 && substr(trim($user_filter), 0, 1)!='(')
$user_filter = '('.trim($user_filter).')';
$query = '(&'.$user_filter.'('.$cn.'))';
return $this->QueryArray($query);
}
function specialchars($str)
{
$from = Array("\\", ',', '+', '"', '<', '>', ';', "\n", "\r", '=', '*');
$to = Array('\5C', '\2C', '\2B', '\22', '\3C', '\3E', '\3B', '\0A', '\0D', '\3D', '\*');
return str_replace($from, $to, $str);
}
function OnExternalAuthList()
{
$arResult = Array();
$db_ldap_serv = CLdapServer::GetList();
while($arLDAP = $db_ldap_serv->Fetch())
{
$arResult[] = Array(
'ID' => 'LDAP#'.$arLDAP['ID'],
'NAME' => $arLDAP['NAME']
);
}
return $arResult;
}
static function NTLMAuth()
{
global $USER;
if ($USER->IsAuthorized())
return;
if(!array_key_exists("AUTH_TYPE", $_SERVER) || ($_SERVER["AUTH_TYPE"] != "NTLM" && $_SERVER["AUTH_TYPE"] != "Negotiate"))
return;
$ntlm_varname = trim(COption::GetOptionString('ldap', 'ntlm_varname', 'REMOTE_USER'));
if (array_key_exists($ntlm_varname, $_SERVER) && strlen($LOGIN = $_SERVER[$ntlm_varname]) > 0)
{
$DOMAIN = "";
if(($pos = strpos($LOGIN, "\\")) !== false)
{
$DOMAIN = substr($LOGIN, 0, $pos);
$LOGIN = substr($LOGIN, $pos + 1);
}
elseif($_SERVER["AUTH_TYPE"] == "Negotiate" && (($pos = strpos($LOGIN, "@")) !== false))
{
$LOGIN = substr($LOGIN, 0, $pos);
$DOMAIN = substr($LOGIN, $pos + 1);
}
$arFilterServer = array('ACTIVE' => 'Y');
if(strlen($DOMAIN) > 0)
{
$arFilterServer['CODE'] = $DOMAIN;
}
else
{
$DEF_DOMAIN_ID = intval(COption::GetOptionInt('ldap', 'ntlm_default_server', 0));
if($DEF_DOMAIN_ID > 0)
$arFilterServer['ID'] = $DEF_DOMAIN_ID;
else
return;
}
$db_ldap_serv = CLdapServer::GetList(Array(), $arFilterServer);
/*@var $xLDAP CLDAP*/
while($xLDAP = $db_ldap_serv->GetNextServer())
{
if($xLDAP->Connect())
{
if($arLdapUser = $xLDAP->FindUser($LOGIN))
{
$ID = $xLDAP->SetUser($arLdapUser, (COption::GetOptionString("ldap", "add_user_when_auth", "Y")=="Y"));
if($ID > 0)
{
$USER->Authorize($ID);
$xLDAP->Disconnect();
return;
}
}
$xLDAP->Disconnect();
}
}
}
}
/**
*
* Recieves the users groups list includes all groups parents list
* searching by memberOf in group properties
* @param $arFindGroups - user groups
* @param $arUserGroups - full array with uppergroups
* @param $arAllGroups - list of all ldap groups
*/
function GetAllMemberOf($arFindGroups, &$arUserGroups, $arAllGroups)
{
if(!$arFindGroups || $arFindGroups=='')
return;
if(!is_array($arFindGroups))
$arFindGroups = Array($arFindGroups);
foreach($arFindGroups as $group_id)
{
if(in_array($group_id, $arUserGroups))
continue;
$arUserGroups[] = $group_id;
$this->GetAllMemberOf($arAllGroups[$group_id]["memberof"], $arUserGroups, $arAllGroups);
}
}
function GetGroupMaps()
{
global $DB;
if(!is_array($this->arGroupMaps))
{
$this->arGroupMaps = array();
$rsCorellations = $DB->Query("SELECT LDAP_GROUP_ID, GROUP_ID FROM b_ldap_group WHERE LDAP_SERVER_ID=".intval($this->arFields['ID']));
while ($arCorellation = $rsCorellations->Fetch())
{
if(!is_array($this->arGroupMaps[$arCorellation["LDAP_GROUP_ID"]]))
$this->arGroupMaps[$arCorellation["LDAP_GROUP_ID"]] = array();
$this->arGroupMaps[$arCorellation["LDAP_GROUP_ID"]][] = $arCorellation["GROUP_ID"];
}
}
return $this->arGroupMaps;
}
//Need this to delete old photo
static function PrepareUserPhoto($uid, &$arLdapUser)
{
if(!isset($arLdapUser["PERSONAL_PHOTO"]))
return false;
$dbRes = CUser::GetById($uid);
$arUser = $dbRes->Fetch();
if(!isset($arUser["PERSONAL_PHOTO"]) || is_null($arUser["PERSONAL_PHOTO"]))
return false;
if($arLdapUser["PERSONAL_PHOTO"] == "")
$arLdapUser["PERSONAL_PHOTO"]["del"] = "Y";
$arLdapUser["PERSONAL_PHOTO"]["old_file"] = $arUser["PERSONAL_PHOTO"];
return true;
}
// update user info, using previously loaded data from AD, make additional calls to AD if needed
function SetUser($arLdapUser, $bAddNew = true)
{
global $USER;
$isHead = false;
$bUSERGen = false;
$userActive = '';
if(!is_object($USER))
{
$USER = new CUser();
$bUSERGen = true;
}
// process previously saved department data
if (IsModuleInstalled('intranet') && is_array($arLdapUser['UF_DEPARTMENT']))
{
$isHead = $arLdapUser['UF_DEPARTMENT']['IS_HEAD'];
// replace temporary value with a real one
$arLdapUser['UF_DEPARTMENT'] = array($arLdapUser['UF_DEPARTMENT']['ID']);
}
if(isset($arLdapUser["ID"]))
{
$ID = intval($arLdapUser["ID"]);
self::PrepareUserPhoto($ID,$arLdapUser);
$USER->Update($ID, $arLdapUser);
}
else
{
$ldapUserID = 0;
if (isset($_REQUEST['ldap_user_id']) && strlen($_REQUEST['ldap_user_id']) == 32)
{
$dbUser = CUser::GetList(
$O='', $B='',
array('XML_ID' => $_REQUEST['ldap_user_id'], 'EXTERNAL_AUTH_ID' => $arLdapUser['EXTERNAL_AUTH_ID']),
array('FIELDS' => array('ID', 'XML_ID'))
);
if ($arUser = $dbUser->Fetch())
{
if($arUser['XML_ID'])
$ldapUserID = $arUser['ID'];
}
}
$bitrixUserId = 0;
$res = CUser::GetList(
$O="", $B="",
array('LOGIN_EQUAL_EXACT' => $arLdapUser['LOGIN']),
array('FIELDS' => array('ID', 'EXTERNAL_AUTH_ID', 'ACTIVE'))
);
while($ar_res = $res->Fetch())
{
if($ar_res['EXTERNAL_AUTH_ID'] == $arLdapUser['EXTERNAL_AUTH_ID'])
{
$bitrixUserId = $ar_res['ID'];
$userActive = $ar_res['ACTIVE'];
break;
}
else
{
$bAddNew = ($bAddNew && COption::GetOptionString("ldap", "ldap_create_duplicate_login_user", 'Y') == 'Y');
}
}
$arLdapUser['PASSWORD'] = uniqid(rand(), true);
if($bitrixUserId <= 0 && $ldapUserID <= 0)
{
if($bAddNew)
{
if(strlen($arLdapUser["EMAIL"]) <= 0)
{
$arLdapUser["EMAIL"] = COption::GetOptionString("ldap", "default_email", 'no@email');
}
$ID = $USER->Add($arLdapUser);
}
else
{
$ID = 0;
}
}
else
{
$ID = ($ldapUserID > 1 ? $ldapUserID : $bitrixUserId);
self::PrepareUserPhoto($ID,$arLdapUser);
//Performance to skip \CIntranetEventHandlers::UpdateActivity();
if(isset($arLdapUser['ACTIVE']) && $userActive == $arLdapUser['ACTIVE'])
unset($arLdapUser['ACTIVE']);
$USER->Update($ID, $arLdapUser);
}
$ID = intval($ID);
}
// - add this user to groups
if ($ID > 0)
{
// - set as head of department
if (IsModuleInstalled('intranet') && $isHead)
{
CLdapUtil::SetDepartmentHead($ID,$arLdapUser['UF_DEPARTMENT'][0]);
}
$arGroupMaps = $this->GetGroupMaps();
if(!empty($arGroupMaps))
{
// For each group finding all superior ones
$arUserLdapGroups = Array();
$arLdapGroups = $this->GetGroupListArray();
$this->GetAllMemberOf($arLdapUser['LDAP_GROUPS'], $arUserLdapGroups, $arLdapGroups);
$arGroupMaps = $this->GetGroupMaps();
$arUserBitrixGroups = $USER->GetUserGroup($ID);
$arUserBitrixGroupsNew = array();
$prevGroups = $arUserBitrixGroups;
sort($prevGroups);
foreach($arGroupMaps as $fromLdapGroup=>$arToUserGroups)
{
foreach($arToUserGroups as $toUserGroup)
{
if (($k = array_search($toUserGroup, $arUserBitrixGroups)) !== false)
{
unset($arUserBitrixGroups[$k]);
}
// If there is such a group among user's
if (in_array($fromLdapGroup, $arUserLdapGroups))
{
$arUserBitrixGroupsNew[] = $toUserGroup;
}
}
}
$arUserBitrixGroups = array_merge($arUserBitrixGroups, array_unique($arUserBitrixGroupsNew));
sort($arUserBitrixGroups);
if($arUserBitrixGroups <> $prevGroups)
{
$USER->SetUserGroup($ID, $arUserBitrixGroups);
}
}
}
if($bUSERGen)
{
unset($USER);
}
return $ID;
}
protected function getLastErrorDescription()
{
$result = '';
if($this->conn)
{
$ldapError = ldap_error($this->conn);
if(strlen($ldapError) > 0)
$result = "\nldap_error: '".$ldapError."'\nldap_errno: '".ldap_errno($this->conn)."'";
}
return $result;
}
}