1<?php 2/* 3 +-----------------------------------------------------------------------------+ 4 | ILIAS open source | 5 +-----------------------------------------------------------------------------+ 6 | Copyright (c) 1998-2006 ILIAS open source, University of Cologne | 7 | | 8 | This program is free software; you can redistribute it and/or | 9 | modify it under the terms of the GNU General Public License | 10 | as published by the Free Software Foundation; either version 2 | 11 | of the License, or (at your option) any later version. | 12 | | 13 | This program is distributed in the hope that it will be useful, | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 | GNU General Public License for more details. | 17 | | 18 | You should have received a copy of the GNU General Public License | 19 | along with this program; if not, write to the Free Software | 20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 21 +-----------------------------------------------------------------------------+ 22*/ 23 24include_once('Services/LDAP/classes/class.ilLDAPServer.php'); 25 26/** 27* 28* @author Stefan Meyer <meyer@leifos.com> 29* @version $Id$ 30* 31* 32* @ingroup ServicesLDAP 33*/ 34class ilLDAPRoleGroupMapping 35{ 36 /** 37 * @var ilLogger 38 */ 39 private $log = null; 40 private static $instance = null; 41 private $servers = null; 42 private $mappings = array(); 43 private $mapping_members = array(); 44 private $query = array(); 45 private $active_servers = false; 46 47 /** 48 * @var array 49 */ 50 private $users = []; 51 52 /** 53 * Singleton contructor 54 * 55 * @access private 56 * 57 */ 58 private function __construct() 59 { 60 global $DIC; 61 62 $this->log = $DIC->logger()->auth(); 63 64 $this->initServers(); 65 } 66 67 /** 68 * Get singleton instance of this class 69 * 70 * @access public 71 * 72 */ 73 public static function _getInstance() 74 { 75 if (is_object(self::$instance)) { 76 return self::$instance; 77 } 78 return self::$instance = new ilLDAPRoleGroupMapping(); 79 } 80 81 /** 82 * Get info string for object 83 * If check info type is enabled this function will check if the info string is visible in the repository. 84 * 85 * @access public 86 * @param int object id 87 * @param bool check info type 88 * 89 */ 90 public function getInfoStrings($a_obj_id, $a_check_type = false) 91 { 92 if (!$this->active_servers) { 93 return false; 94 } 95 if ($a_check_type) { 96 if (isset($this->mapping_info_strict[$a_obj_id]) and is_array($this->mapping_info_strict[$a_obj_id])) { 97 return $this->mapping_info_strict[$a_obj_id]; 98 } 99 } else { 100 if (isset($this->mapping_info[$a_obj_id]) and is_array($this->mapping_info[$a_obj_id])) { 101 return $this->mapping_info[$a_obj_id]; 102 } 103 } 104 return false; 105 } 106 107 108 /** 109 * This method is typically called from class RbacAdmin::assignUser() 110 * It checks if there is a role mapping and if the user has auth mode LDAP 111 * After these checks the user is assigned to the LDAP group 112 * 113 * @access public 114 * @param 115 * 116 */ 117 public function assign($a_role_id, $a_usr_id) 118 { 119 // return if there nothing to do 120 if (!$this->active_servers) { 121 return false; 122 } 123 124 if (!$this->isHandledRole($a_role_id)) { 125 return false; 126 } 127 if (!$this->isHandledUser($a_usr_id)) { 128 $this->log->info('LDAP assign: User ID: ' . $a_usr_id . ' has no LDAP account'); 129 return false; 130 } 131 $this->log->info('LDAP assigned: User ID: ' . $a_usr_id . ' Role Id: ' . $a_role_id); 132 $this->assignToGroup($a_role_id, $a_usr_id); 133 134 return true; 135 } 136 137 /** 138 * Delete role. 139 * This function triggered from ilRbacAdmin::deleteRole 140 * It deassigns all user from the mapped ldap group. 141 * 142 * @access public 143 * @param int role id 144 * 145 */ 146 public function deleteRole($a_role_id) 147 { 148 global $DIC; 149 150 $rbacreview = $DIC['rbacreview']; 151 152 // return if there nothing to do 153 if (!$this->active_servers) { 154 return false; 155 } 156 157 if (!$this->isHandledRole($a_role_id)) { 158 return false; 159 } 160 161 foreach ($rbacreview->assignedUsers($a_role_id) as $usr_id) { 162 $this->deassign($a_role_id, $usr_id); 163 } 164 return true; 165 } 166 167 168 /** 169 * This method is typically called from class RbacAdmin::deassignUser() 170 * It checks if there is a role mapping and if the user has auth mode LDAP 171 * After these checks the user is deassigned from the LDAP group 172 * 173 * @access public 174 * @param 175 * 176 */ 177 public function deassign($a_role_id, $a_usr_id) 178 { 179 // return if there notzing to do 180 if (!$this->active_servers) { 181 return false; 182 } 183 if (!$this->isHandledRole($a_role_id)) { 184 return false; 185 } 186 if (!$this->isHandledUser($a_usr_id)) { 187 return false; 188 } 189 $this->log->info('LDAP deassigned: User ID: ' . $a_usr_id . ' Role Id: ' . $a_role_id); 190 $this->deassignFromGroup($a_role_id, $a_usr_id); 191 192 return true; 193 } 194 195 /** 196 * Delete user => deassign from all ldap groups 197 * 198 * @access public 199 * @param int user id 200 */ 201 public function deleteUser($a_usr_id) 202 { 203 foreach ($this->mappings as $role_id => $data) { 204 $this->deassign($role_id, $a_usr_id); 205 } 206 return true; 207 } 208 209 210 /** 211 * Check if there is any active server with 212 * 213 * @access private 214 * @param 215 * 216 */ 217 private function initServers() 218 { 219 $server_ids = ilLDAPServer::_getRoleSyncServerIds(); 220 221 if (!count($server_ids)) { 222 return false; 223 } 224 225 // Init servers 226 include_once('Services/LDAP/classes/class.ilLDAPRoleGroupMappingSettings.php'); 227 228 $this->active_servers = true; 229 $this->mappings = array(); 230 $this->users = []; 231 foreach ($server_ids as $server_id) { 232 $this->servers[$server_id] = new ilLDAPServer($server_id); 233 $this->mappings = ilLDAPRoleGroupMappingSettings::_getAllActiveMappings(); 234 $this->users[$server_id] = ilObjUser::_getExternalAccountsByAuthMode( 235 'ldap_' . $server_id, 236 true 237 ); 238 } 239 $this->mapping_info = array(); 240 $this->mapping_info_strict = array(); 241 foreach ($this->mappings as $mapping) { 242 foreach ($mapping as $key => $data) { 243 if (strlen($data['info']) and $data['object_id']) { 244 $this->mapping_info[$data['object_id']][] = $data['info']; 245 } 246 if (strlen($data['info']) && ($data['info_type'] == ilLDAPRoleGroupMappingSettings::MAPPING_INFO_ALL)) { 247 $this->mapping_info_strict[$data['object_id']][] = $data['info']; 248 } 249 } 250 } 251 return true; 252 } 253 254 /** 255 * Check if a role is handled or not 256 * 257 * @access private 258 * @param int role_id 259 * @return int server id or 0 if mapping exists 260 * 261 */ 262 private function isHandledRole($a_role_id) 263 { 264 return array_key_exists($a_role_id, $this->mappings); 265 } 266 267 /** 268 * Check if user is ldap user 269 * 270 * @access private 271 */ 272 private function isHandledUser($a_usr_id) 273 { 274 foreach ($this->users as $server_id => $users) { 275 if (array_key_exists($a_usr_id, $users)) { 276 return true; 277 } 278 } 279 return false; 280 } 281 282 283 /** 284 * Assign user to group 285 * 286 * @access private 287 * @param int role_id 288 * @param int user_id 289 */ 290 private function assignToGroup($a_role_id, $a_usr_id) 291 { 292 foreach ($this->mappings[$a_role_id] as $data) { 293 try { 294 if ($data['isdn']) { 295 $external_account = $this->readDN($a_usr_id, $data['server_id']); 296 } else { 297 $external_account = $this->users[$data['server_id']][$a_usr_id]; 298 } 299 // Forcing modAdd since Active directory is too slow and i cannot check if a user is member or not. 300 $query_obj = $this->getLDAPQueryInstance($data['server_id'], $data['url']); 301 $query_obj->modAdd($data['dn'], array($data['member'] => $external_account)); 302 $this->log->info('LDAP assign: Assigned ' . $external_account . ' to group ' . $data['dn']); 303 } catch (ilLDAPQueryException $exc) { 304 $this->log->warning($exc->getMessage()); 305 // try next mapping 306 continue; 307 } 308 } 309 } 310 311 /** 312 * Deassign user from group 313 * 314 * @access private 315 * @param int role_id 316 * @param int user_id 317 * 318 */ 319 private function deassignFromGroup($a_role_id, $a_usr_id) 320 { 321 foreach ($this->mappings[$a_role_id] as $data) { 322 try { 323 if ($data['isdn']) { 324 $external_account = $this->readDN($a_usr_id, $data['server_id']); 325 } else { 326 $external_account = $this->users[$data['server_id']][$a_usr_id]; 327 } 328 329 // Check for other role membership 330 if ($role_id = $this->checkOtherMembership($a_usr_id, $a_role_id, $data)) { 331 $this->log->info('LDAP deassign: User is still assigned to role "' . $role_id . '".'); 332 continue; 333 } 334 // Deassign user 335 $query_obj = $this->getLDAPQueryInstance($data['server_id'], $data['url']); 336 $query_obj->modDelete($data['dn'], array($data['member'] => $external_account)); 337 $this->log->info('LDAP deassign: Deassigned ' . $external_account . ' from group ' . $data['dn']); 338 339 // Delete from cache 340 if (is_array($this->mapping_members[$data['mapping_id']])) { 341 $key = array_search($external_account, $this->mapping_members[$data['mapping_id']]); 342 if ($key or $key === 0) { 343 unset($this->mapping_members[$data['mapping_id']]); 344 } 345 } 346 } catch (ilLDAPQueryException $exc) { 347 $this->log->warning($exc->getMessage()); 348 // try next mapping 349 continue; 350 } 351 } 352 } 353 354 355 /** 356 * Check other membership 357 * 358 * @access private 359 * @return string role name 360 * 361 */ 362 private function checkOtherMembership($a_usr_id, $a_role_id, $a_data) 363 { 364 global $DIC; 365 366 $rbacreview = $DIC['rbacreview']; 367 $ilObjDataCache = $DIC['ilObjDataCache']; 368 369 foreach ($this->mappings as $role_id => $tmp_data) { 370 foreach ($tmp_data as $data) { 371 if ($role_id == $a_role_id) { 372 continue; 373 } 374 if ($data['server_id'] != $a_data['server_id']) { 375 continue; 376 } 377 if ($data['dn'] != $a_data['dn']) { 378 continue; 379 } 380 if ($rbacreview->isAssigned($a_usr_id, $role_id)) { 381 return $ilObjDataCache->lookupTitle($role_id); 382 } 383 } 384 } 385 return false; 386 } 387 388 /** 389 * Store Members 390 * 391 * @access private 392 * 393 */ 394 private function storeMembers($a_mapping_id, $a_data) 395 { 396 $this->mapping_members[$a_mapping_id] = array(); 397 foreach ($a_data as $field => $value) { 398 if (strtolower($field) == 'dn') { 399 continue; 400 } 401 402 if (!is_array($value)) { 403 $this->mapping_members[$a_mapping_id][] = $value; 404 continue; 405 } 406 foreach ($value as $external_account) { 407 $this->mapping_members[$a_mapping_id][] = $external_account; 408 } 409 } 410 return true; 411 } 412 413 /** 414 * Read DN of user 415 * 416 * @access private 417 * @param int user id 418 * @param int server id 419 * @throws ilLDAPQueryException 420 */ 421 private function readDN($a_usr_id, $a_server_id) 422 { 423 if (isset($this->user_dns[$a_usr_id])) { 424 return $this->user_dns[$a_usr_id]; 425 } 426 427 $external_account = $this->users[$a_server_id][$a_usr_id]; 428 429 try { 430 $server = $this->servers[$a_server_id]; 431 $query_obj = $this->getLDAPQueryInstance($a_server_id, $server->getUrl()); 432 433 if ($search_base = $server->getSearchBase()) { 434 $search_base .= ','; 435 } 436 $search_base .= $server->getBaseDN(); 437 438 // try optional group user filter first 439 if ($server->isMembershipOptional() and $server->getGroupUserFilter()) { 440 $userFilter = $server->getGroupUserFilter(); 441 } else { 442 $userFilter = $server->getFilter(); 443 } 444 445 $filter = sprintf( 446 '(&(%s=%s)%s)', 447 $server->getUserAttribute(), 448 $external_account, 449 $userFilter 450 ); 451 452 $res = $query_obj->query($search_base, $filter, $server->getUserScope(), array('dn')); 453 454 if (!$res->numRows()) { 455 include_once('Services/LDAP/classes/class.ilLDAPQueryException.php'); 456 throw new ilLDAPQueryException(__METHOD__ . ' cannot find dn for user ' . $external_account); 457 } 458 if ($res->numRows() > 1) { 459 include_once('Services/LDAP/classes/class.ilLDAPQueryException.php'); 460 throw new ilLDAPQueryException(__METHOD__ . ' found multiple distinguished name for: ' . $external_account); 461 } 462 463 $data = $res->get(); 464 return $this->user_dns[$a_usr_id] = $data['dn']; 465 } catch (ilLDAPQueryException $exc) { 466 throw $exc; 467 } 468 } 469 470 /** 471 * Get LDAPQueryInstance 472 * 473 * @access private 474 * @param 475 * @throws ilLDAPQueryException 476 */ 477 private function getLDAPQueryInstance($a_server_id, $a_url) 478 { 479 include_once 'Services/LDAP/classes/class.ilLDAPQuery.php'; 480 481 if (array_key_exists($a_server_id, $this->query) and 482 array_key_exists($a_url, $this->query[$a_server_id]) and 483 is_object($this->query[$a_server_id][$a_url])) { 484 return $this->query[$a_server_id][$a_url]; 485 } 486 try { 487 $tmp_query = new ilLDAPQuery($this->servers[$a_server_id], $a_url); 488 $tmp_query->bind(IL_LDAP_BIND_ADMIN); 489 } catch (ilLDAPQueryException $exc) { 490 throw $exc; 491 } 492 return $this->query[$a_server_id][$a_url] = $tmp_query; 493 } 494} 495