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 24/** 25* Singleton class that stores all security settings 26* 27* @author Roland Küstermann <roland@kuestermann.com> 28* @version $Id$ 29* 30* 31* @ingroup Services/PrivacySecurity 32*/ 33 34class ilSecuritySettings 35{ 36 public static $SECURITY_SETTINGS_ERR_CODE_AUTO_HTTPS = 1; 37 public static $SECURITY_SETTINGS_ERR_CODE_HTTP_NOT_AVAILABLE = 2; 38 public static $SECURITY_SETTINGS_ERR_CODE_HTTPS_NOT_AVAILABLE = 3; 39 40 const SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MIN_LENGTH = 4; 41 const SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MAX_LENGTH = 5; 42 const SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MAX_AGE = 6; 43 const SECURITY_SETTINGS_ERR_CODE_INVALID_LOGIN_MAX_ATTEMPTS = 7; 44 const SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN1 = 11; 45 const SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN2 = 8; 46 const SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN3 = 9; 47 const SECURITY_SETTINGS_ERR_CODE_PASSWORD_MAX_LENGTH_LESS_MIN_LENGTH = 10; 48 49 private static $instance = null; 50 private $db; 51 private $settings; 52 53 private $https_enable; 54 55 const DEFAULT_PASSWORD_CHARS_AND_NUMBERS_ENABLED = true; 56 const DEFAULT_PASSWORD_SPECIAL_CHARS_ENABLED = false; 57 const DEFAULT_PASSWORD_MIN_LENGTH = 8; 58 const DEFAULT_PASSWORD_MAX_LENGTH = 0; 59 const DEFAULT_PASSWORD_MAX_AGE = 90; 60 const DEFAULT_LOGIN_MAX_ATTEMPTS = 5; 61 62 const DEFAULT_PASSWORD_CHANGE_ON_FIRST_LOGIN_ENABLED = false; 63 const DEFAULT_PREVENT_SIMULTANEOUS_LOGINS = false; 64 65 private $password_chars_and_numbers_enabled = self::DEFAULT_PASSWORD_CHARS_AND_NUMBERS_ENABLED; 66 private $password_special_chars_enabled = self::DEFAULT_PASSWORD_SPECIAL_CHARS_ENABLED; 67 private $password_min_length = self::DEFAULT_PASSWORD_MIN_LENGTH; 68 private $password_max_length = self::DEFAULT_PASSWORD_MAX_LENGTH; 69 private $password_max_age = self::DEFAULT_PASSWORD_MAX_AGE; 70 private $password_ucase_chars_num = 0; 71 private $password_lcase_chars_num = 0; 72 private $login_max_attempts = self::DEFAULT_LOGIN_MAX_ATTEMPTS; 73 private $password_must_not_contain_loginname = false; 74 75 private $password_change_on_first_login_enabled = self::DEFAULT_PASSWORD_CHANGE_ON_FIRST_LOGIN_ENABLED; 76 private $prevent_simultaneous_logins = self::DEFAULT_PREVENT_SIMULTANEOUS_LOGINS; 77 78 private $protect_admin_role = false; 79 80 /** 81 * Private constructor: use _getInstance() 82 * 83 * @access private 84 * @param 85 * 86 */ 87 private function __construct() 88 { 89 global $DIC; 90 91 $ilSetting = $DIC['ilSetting']; 92 $ilDB = $DIC['ilDB']; 93 94 $this->db = $ilDB; 95 $this->settings = $ilSetting; 96 97 $this->read(); 98 } 99 100 /** 101 * Get instance of ilSecuritySettings 102 * 103 * @return ilSecuritySettings instance 104 * @access public 105 * 106 */ 107 public static function _getInstance() 108 { 109 if (is_object(self::$instance)) { 110 return self::$instance; 111 } 112 return self::$instance = new ilSecuritySettings(); 113 } 114 115 public function getSecuritySettingsRefId() 116 { 117 return $this->ref_id; 118 } 119 120 /** 121 * set if the passwords have to contain 122 * characters and numbers 123 * 124 * @param boolean $a_chars_and_numbers_enabled 125 * 126 */ 127 public function setPasswordCharsAndNumbersEnabled($a_chars_and_numbers_enabled) 128 { 129 $this->password_chars_and_numbers_enabled = $a_chars_and_numbers_enabled; 130 } 131 132 /** 133 * get boolean if the passwords have to contain 134 * characters and numbers 135 * 136 * @return boolean characters and numbers enabled 137 * 138 */ 139 public function isPasswordCharsAndNumbersEnabled() 140 { 141 return $this->password_chars_and_numbers_enabled; 142 } 143 144 /** 145 * set if the passwords have to contain 146 * special characters 147 * 148 * @param boolean $a_password_special_chars_enabled 149 * 150 */ 151 public function setPasswordSpecialCharsEnabled($a_password_special_chars_enabled) 152 { 153 $this->password_special_chars_enabled = $a_password_special_chars_enabled; 154 } 155 156 /** 157 * get boolean if the passwords have to contain 158 * special characters 159 * 160 * @return boolean password special chars enabled 161 * 162 */ 163 public function isPasswordSpecialCharsEnabled() 164 { 165 return $this->password_special_chars_enabled; 166 } 167 168 /** 169 * set the minimum length for passwords 170 * 171 * @param integer $a_password_min_length 172 */ 173 public function setPasswordMinLength($a_password_min_length) 174 { 175 $this->password_min_length = $a_password_min_length; 176 } 177 178 /** 179 * get the minimum length for passwords 180 * 181 * @return integer password min length 182 */ 183 public function getPasswordMinLength() 184 { 185 return $this->password_min_length; 186 } 187 188 /** 189 * set the maximum length for passwords 190 * 191 * @param integer $a_password_max_length 192 */ 193 public function setPasswordMaxLength($a_password_max_length) 194 { 195 $this->password_max_length = $a_password_max_length; 196 } 197 198 /** 199 * get the maximum length for passwords 200 * 201 * @return integer password max length 202 */ 203 public function getPasswordMaxLength() 204 { 205 return $this->password_max_length; 206 } 207 208 /** 209 * set the maximum password age 210 * 211 * @param integer $a_password_max_age 212 */ 213 public function setPasswordMaxAge($a_password_max_age) 214 { 215 $this->password_max_age = $a_password_max_age; 216 } 217 218 /** 219 * get the maximum password age 220 * 221 * @return integer password max age 222 */ 223 public function getPasswordMaxAge() 224 { 225 return $this->password_max_age; 226 } 227 228 /** 229 * set the maximum count of login attempts 230 * 231 * @param integer $a_login_max_attempts 232 */ 233 public function setLoginMaxAttempts($a_login_max_attempts) 234 { 235 $this->login_max_attempts = $a_login_max_attempts; 236 } 237 238 /** 239 * get the maximum count of login attempts 240 * 241 * @return integer password max login attempts 242 */ 243 public function getLoginMaxAttempts() 244 { 245 return $this->login_max_attempts; 246 } 247 248 /** 249 * Enable https for certain scripts 250 * 251 * @param boolean $value 252 */ 253 public function setHTTPSEnabled($value) 254 { 255 $this->https_enable = $value; 256 } 257 258 /** 259 * read access to https enabled property 260 * 261 * @return boolean true, if enabled, false otherwise 262 */ 263 public function isHTTPSEnabled() 264 { 265 return $this->https_enable; 266 } 267 268 /** 269 * set if the passwords have to be changed by users 270 * on first login 271 * 272 * @param boolean $a_password_change_on_first_login_enabled 273 * 274 */ 275 public function setPasswordChangeOnFirstLoginEnabled($a_password_change_on_first_login_enabled) 276 { 277 $this->password_change_on_first_login_enabled = $a_password_change_on_first_login_enabled; 278 } 279 280 /** 281 * get boolean if the passwords have to be changed by users 282 * on first login 283 * 284 * @return boolean password change on first login enabled 285 * 286 */ 287 public function isPasswordChangeOnFirstLoginEnabled() 288 { 289 return $this->password_change_on_first_login_enabled; 290 } 291 292 /** 293 * Check if admin role is protected 294 * @return type 295 */ 296 public function isAdminRoleProtected() 297 { 298 return (bool) $this->protect_admin_role; 299 } 300 301 /** 302 * Set admin role protection status 303 * @param type $a_stat 304 */ 305 public function protectedAdminRole($a_stat) 306 { 307 $this->protect_admin_role = $a_stat; 308 } 309 310 /** 311 * Check if the administrator role is accessible for a specific user 312 * @param int $a_usr_id 313 */ 314 public function checkAdminRoleAccessible($a_usr_id) 315 { 316 global $DIC; 317 318 $rbacreview = $DIC['rbacreview']; 319 320 if (!$this->isAdminRoleProtected()) { 321 return true; 322 } 323 if ($rbacreview->isAssigned($a_usr_id, SYSTEM_ROLE_ID)) { 324 return true; 325 } 326 return false; 327 } 328 329 /** 330 * Save settings 331 * 332 * 333 */ 334 public function save() 335 { 336 $this->settings->set('https', (int) $this->isHTTPSEnabled()); 337 338 $this->settings->set('ps_password_chars_and_numbers_enabled', (bool) $this->isPasswordCharsAndNumbersEnabled()); 339 $this->settings->set('ps_password_special_chars_enabled', (bool) $this->isPasswordSpecialCharsEnabled()); 340 $this->settings->set('ps_password_min_length', (int) $this->getPasswordMinLength()); 341 $this->settings->set('ps_password_max_length', (int) $this->getPasswordMaxLength()); 342 $this->settings->set('ps_password_max_age', (int) $this->getPasswordMaxAge()); 343 $this->settings->set('ps_login_max_attempts', (int) $this->getLoginMaxAttempts()); 344 $this->settings->set('ps_password_uppercase_chars_num', (int) $this->getPasswordNumberOfUppercaseChars()); 345 $this->settings->set('ps_password_lowercase_chars_num', (int) $this->getPasswordNumberOfLowercaseChars()); 346 $this->settings->set('ps_password_must_not_contain_loginame', (int) $this->getPasswordMustNotContainLoginnameStatus()); 347 348 $this->settings->set('ps_password_change_on_first_login_enabled', (bool) $this->isPasswordChangeOnFirstLoginEnabled()); 349 $this->settings->set('ps_prevent_simultaneous_logins', (int) $this->isPreventionOfSimultaneousLoginsEnabled()); 350 $this->settings->set('ps_protect_admin', (int) $this->isAdminRoleProtected()); 351 } 352 /** 353 * read settings 354 * 355 * @access private 356 * @param 357 * 358 */ 359 private function read() 360 { 361 global $DIC; 362 363 $ilDB = $DIC['ilDB']; 364 365 $query = "SELECT object_reference.ref_id FROM object_reference,tree,object_data " . 366 "WHERE tree.parent = " . $ilDB->quote(SYSTEM_FOLDER_ID, 'integer') . " " . 367 "AND object_data.type = 'ps' " . 368 "AND object_reference.ref_id = tree.child " . 369 "AND object_reference.obj_id = object_data.obj_id"; 370 $res = $this->db->query($query); 371 $row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC); 372 $this->ref_id = $row["ref_id"]; 373 374 $this->https_enable = (boolean) $this->settings->get('https', false); 375 376 $this->password_chars_and_numbers_enabled = (bool) $this->settings->get('ps_password_chars_and_numbers_enabled', self::DEFAULT_PASSWORD_CHARS_AND_NUMBERS_ENABLED); 377 $this->password_special_chars_enabled = (bool) $this->settings->get('ps_password_special_chars_enabled', self::DEFAULT_PASSWORD_SPECIAL_CHARS_ENABLED); 378 $this->password_min_length = (int) $this->settings->get('ps_password_min_length', self::DEFAULT_PASSWORD_MIN_LENGTH); 379 $this->password_max_length = (int) $this->settings->get('ps_password_max_length', self::DEFAULT_PASSWORD_MAX_LENGTH); 380 $this->password_max_age = (int) $this->settings->get('ps_password_max_age', self::DEFAULT_PASSWORD_MAX_AGE); 381 $this->login_max_attempts = (int) $this->settings->get('ps_login_max_attempts', self::DEFAULT_LOGIN_MAX_ATTEMPTS); 382 $this->password_ucase_chars_num = (int) $this->settings->get('ps_password_uppercase_chars_num', 0); 383 $this->password_lcase_chars_num = (int) $this->settings->get('ps_password_lowercase_chars_num', 0); 384 $this->password_must_not_contain_loginname = $this->settings->get('ps_password_must_not_contain_loginame', 0) == '1' ? true : false; 385 386 $this->password_change_on_first_login_enabled = (bool) $this->settings->get('ps_password_change_on_first_login_enabled', self::DEFAULT_PASSWORD_CHANGE_ON_FIRST_LOGIN_ENABLED); 387 $this->prevent_simultaneous_logins = (bool) $this->settings->get('ps_prevent_simultaneous_logins', self::DEFAULT_PREVENT_SIMULTANEOUS_LOGINS); 388 389 $this->protect_admin_role = (bool) $this->settings->get('ps_protect_admin', $this->protect_admin_role); 390 } 391 392 /** 393 * validate settings 394 * 395 * @return 0, if everything is ok, an error code otherwise 396 */ 397 public function validate(ilPropertyFormGUI $a_form = null) 398 { 399 $code = null; 400 401 if ($a_form) { 402 include_once "Services/PrivacySecurity/classes/class.ilObjPrivacySecurityGUI.php"; 403 } 404 405 include_once './Services/Http/classes/class.ilHTTPS.php'; 406 407 if ($this->isHTTPSEnabled()) { 408 if (!ilHTTPS::_checkHTTPS()) { 409 $code = ilSecuritySettings::$SECURITY_SETTINGS_ERR_CODE_HTTPS_NOT_AVAILABLE; 410 if (!$a_form) { 411 return $code; 412 } else { 413 $a_form->getItemByPostVar('https_enabled') 414 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 415 } 416 } 417 } 418 419 if ($this->getPasswordMinLength() < 0) { 420 $code = self::SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MIN_LENGTH; 421 if (!$a_form) { 422 return $code; 423 } else { 424 $a_form->getItemByPostVar('password_min_length') 425 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 426 } 427 } 428 429 if ($this->getPasswordMaxLength() < 0) { 430 $code = self::SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MAX_LENGTH; 431 if (!$a_form) { 432 return $code; 433 } else { 434 $a_form->getItemByPostVar('password_max_length') 435 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 436 } 437 } 438 439 $password_min_length = 1; 440 441 if ($this->getPasswordNumberOfUppercaseChars() > 0 || $this->getPasswordNumberOfLowercaseChars() > 0) { 442 $password_min_length = 0; 443 if ($this->getPasswordNumberOfUppercaseChars() > 0) { 444 $password_min_length += $this->getPasswordNumberOfUppercaseChars(); 445 } 446 if ($this->getPasswordNumberOfLowercaseChars() > 0) { 447 $password_min_length += $this->getPasswordNumberOfLowercaseChars(); 448 } 449 $password_min_length_error_code = self::SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN1; 450 } 451 452 if ($this->isPasswordCharsAndNumbersEnabled()) { 453 $password_min_length++; 454 $password_min_length_error_code = self::SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN2; 455 456 if ($this->isPasswordSpecialCharsEnabled()) { 457 $password_min_length++; 458 $password_min_length_error_code = self::SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN3; 459 } 460 } elseif ($password_min_length > 1 && $this->isPasswordSpecialCharsEnabled()) { 461 $password_min_length++; 462 $password_min_length_error_code = self::SECURITY_SETTINGS_ERR_CODE_PASSWORD_MIN_LENGTH_MIN3; 463 } 464 465 if ($this->getPasswordMinLength() > 0 && $this->getPasswordMinLength() < $password_min_length) { 466 $code = $password_min_length_error_code; 467 if (!$a_form) { 468 return $code; 469 } else { 470 $a_form->getItemByPostVar('password_min_length') 471 ->setAlert(sprintf(ilObjPrivacySecurityGUI::getErrorMessage($code), $password_min_length)); 472 } 473 } 474 if ($this->getPasswordMaxLength() > 0 && $this->getPasswordMaxLength() < $this->getPasswordMinLength()) { 475 $code = self::SECURITY_SETTINGS_ERR_CODE_PASSWORD_MAX_LENGTH_LESS_MIN_LENGTH; 476 if (!$a_form) { 477 return $code; 478 } else { 479 $a_form->getItemByPostVar('password_max_length') 480 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 481 } 482 } 483 484 if ($this->getPasswordMaxAge() < 0) { 485 $code = self::SECURITY_SETTINGS_ERR_CODE_INVALID_PASSWORD_MAX_AGE; 486 if (!$a_form) { 487 return $code; 488 } else { 489 $a_form->getItemByPostVar('password_max_age') 490 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 491 } 492 } 493 494 if ($this->getLoginMaxAttempts() < 0) { 495 $code = self::SECURITY_SETTINGS_ERR_CODE_INVALID_LOGIN_MAX_ATTEMPTS; 496 if (!$a_form) { 497 return $code; 498 } else { 499 $a_form->getItemByPostVar('login_max_attempts') 500 ->setAlert(ilObjPrivacySecurityGUI::getErrorMessage($code)); 501 } 502 } 503 504 /* 505 * todo: have to check for local auth if first login password change is enabled?? 506 * than: add errorcode 507 */ 508 509 if (!$a_form) { 510 return 0; 511 } else { 512 return !(bool) $code; 513 } 514 } 515 516 /** 517 * Prevention of simultaneous logins with the same account 518 * 519 * @return boolean true, if prevention of simultaneous logins with the same account is enabled, false otherwise 520 */ 521 public function isPreventionOfSimultaneousLoginsEnabled() 522 { 523 return (bool) $this->prevent_simultaneous_logins; 524 } 525 526 /** 527 * Enable/Disable prevention of simultaneous logins with the same account 528 * 529 * @param boolean $value 530 */ 531 public function setPreventionOfSimultaneousLogins($value) 532 { 533 $this->prevent_simultaneous_logins = (bool) $value; 534 } 535 536 /** 537 * Set number of uppercase characters required 538 * @param integer 539 */ 540 public function setPasswordNumberOfUppercaseChars($password_ucase_chars_num) 541 { 542 $this->password_ucase_chars_num = $password_ucase_chars_num; 543 } 544 545 /** 546 * Returns number of uppercase characters required 547 * @return integer 548 */ 549 public function getPasswordNumberOfUppercaseChars() 550 { 551 return $this->password_ucase_chars_num; 552 } 553 554 /** 555 * Set number of lowercase characters required 556 * @param integer 557 */ 558 public function setPasswordNumberOfLowercaseChars($password_lcase_chars_num) 559 { 560 $this->password_lcase_chars_num = $password_lcase_chars_num; 561 } 562 563 /** 564 * Returns number of lowercase characters required 565 * @return integer 566 */ 567 public function getPasswordNumberOfLowercaseChars() 568 { 569 return $this->password_lcase_chars_num; 570 } 571 572 /** 573 * Set whether the password must not contain the loginname or not 574 * @param boolean 575 */ 576 public function setPasswordMustNotContainLoginnameStatus($status) 577 { 578 $this->password_must_not_contain_loginname = $status; 579 } 580 581 /** 582 * Return whether the password must not contain the loginname or not 583 * @param boolean 584 */ 585 public function getPasswordMustNotContainLoginnameStatus() 586 { 587 return $this->password_must_not_contain_loginname; 588 } 589} 590