From 807cf5697be5d5ed3540314ffe0937d0c29c5aa1 Mon Sep 17 00:00:00 2001 From: jmontoyaa Date: Fri, 7 Apr 2017 10:38:30 +0200 Subject: [PATCH] Rewrite generate password and check password - requires composer update - Added ircmaxell/random-lib lib to improve security. - Add 'password_requirements' setting see BT#12571 to customize min requirements. --- composer.json | 3 +- main/inc/lib/api.lib.php | 119 +++++++++++++++------- main/inc/lib/internationalization.lib.php | 3 +- main/install/configuration.dist.php | 11 +- 4 files changed, 98 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index 690f8a4337..71cad5ba0d 100755 --- a/composer.json +++ b/composer.json @@ -104,7 +104,8 @@ "kigkonsult/icalcreator" : "0.1.0", "essence/essence": "2.6.1", "pclzip/pclzip": "2.8.2", - "chamilo/chash": "dev-master" + "chamilo/chash": "dev-master", + "ircmaxell/random-lib": "^1.2" }, "require-dev": { "behat/behat": "@stable", diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index 572b4b4dd3..ae3bd4dd31 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -1953,22 +1953,49 @@ function api_format_course_array($course_data) */ function api_generate_password($length = 8) { - $characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'; - $numbers = '23456789'; - if ($length < 2) { $length = 2; } + + $charactersLowerCase = 'abcdefghijkmnopqrstuvwxyz'; + $charactersUpperCase = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; + $minNumbers = 2; + $length = $length - $minNumbers; + $minLowerCase = round($length/2); + $minUpperCase = $length - $minLowerCase; + $password = ''; - for ($i = 0; $i < $length; $i ++) { - $password .= $characters[rand() % strlen($characters)]; + $passwordRequirements = api_get_configuration_value('password_requirements'); + + $factory = new RandomLib\Factory(); + $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM)); + + if (!empty($passwordRequirements)) { + $length = $passwordRequirements['min']['length']; + $minNumbers = $passwordRequirements['min']['numeric']; + $minLowerCase = $passwordRequirements['min']['lowercase']; + $minUpperCase = $passwordRequirements['min']['uppercase']; + + $rest = $length - $minNumbers - $minLowerCase - $minUpperCase; + // Add the rest to fill the length requirement + if ($rest > 0) { + $password .= $generator->generateString($rest, $charactersLowerCase.$charactersUpperCase); + } } - // At least 2 digits - for ($i = 0; $i < 2; $i ++) { - $password .= $numbers[rand() % strlen($numbers)]; + // Min digits default 2 + for ($i = 0; $i < $minNumbers; $i ++) { + $password .= $generator->generateInt(2, 9); } + // Min lowercase + $password .= $generator->generateString($minLowerCase, $charactersLowerCase); + + // Min uppercase + $password .= $generator->generateString($minUpperCase, $charactersUpperCase); + + $password = str_shuffle($password); + return $password; } @@ -1982,37 +2009,57 @@ function api_generate_password($length = 8) * 3. The password should contain at least 3 letters. * 4. It should contain at least 2 digits. * 5. It should not contain 3 or more consequent (according to ASCII table) characters. - */ -function api_check_password($password) { - $password_length = api_strlen($password); - if ($password_length < 5) { + * Settings will change if the configuration value is set: password_requirements + */ +function api_check_password($password) +{ + $passwordRequirements = api_get_configuration_value('password_requirements'); + + $minLength = 5; + $minLetters = 3; + $minNumbers = 2; + $minLowerCase = 0; // Is only use if password_requirements is set + $minUpperCase = 0; // Is only use if password_requirements is set + if (!empty($passwordRequirements)) { + $minLength = $passwordRequirements['min']['length']; + $minNumbers = $passwordRequirements['min']['numeric']; + $minLowerCase = $passwordRequirements['min']['lowercase']; + $minUpperCase = $passwordRequirements['min']['uppercase']; + $minLetters = $minLowerCase + $minUpperCase; + } + $passwordLength = api_strlen($password); + if ($passwordLength < $minLength) { return false; } - $password = api_strtolower($password); - $letters = 0; + $digits = 0; - $consequent_characters = 0; - $previous_character_code = 0; - for ($i = 0; $i < $password_length; $i ++) { - $current_character_code = api_ord(api_substr($password, $i, 1)); - if ($i && abs($current_character_code - $previous_character_code) <= 1) { - $consequent_characters ++; - if ($consequent_characters == 3) { - return false; - } - } else { - $consequent_characters = 1; + $lowerCase = 0; + $upperCase = 0; + + for ($i = 0; $i < $passwordLength; $i++) { + $currentCharacterCode = api_ord(api_substr($password, $i, 1)); + if ($currentCharacterCode >= 65 && $currentCharacterCode <= 90) { + $upperCase++; } - if ($current_character_code >= 97 && $current_character_code <= 122) { - $letters ++; - } elseif ($current_character_code >= 48 && $current_character_code <= 57) { - $digits ++; - } else { - return false; + + if ($currentCharacterCode >= 97 && $currentCharacterCode <= 122) { + $lowerCase++; + } + if ($currentCharacterCode >= 48 && $currentCharacterCode <= 57) { + $digits++; } - $previous_character_code = $current_character_code; } - return ($letters >= 3 && $digits >= 2); + $letters = $upperCase + $lowerCase; + + if (!empty($passwordRequirements)) { + return ( + $upperCase >= $minUpperCase && + $lowerCase >= $minLowerCase && + $digits >= $minNumbers + ); + } + + return $letters >= $minLetters && $digits >= $minNumbers; } /** @@ -2040,7 +2087,8 @@ function api_clear_anonymous($db_check = false) * @author Noel Dieschburg * @param the int status code */ -function get_status_from_code($status_code) { +function get_status_from_code($status_code) +{ switch ($status_code) { case STUDENT: return get_lang('Student', ''); @@ -2062,7 +2110,8 @@ function get_status_from_code($status_code) { * time we get on a course homepage or on a neutral page (index, admin, my space) * @return bool true if set user as anonymous, false if user was already logged in or anonymous id could not be found */ -function api_set_anonymous() { +function api_set_anonymous() +{ global $_user; if (!empty($_user['user_id'])) { diff --git a/main/inc/lib/internationalization.lib.php b/main/inc/lib/internationalization.lib.php index c49820dff1..98c327ab7c 100755 --- a/main/inc/lib/internationalization.lib.php +++ b/main/inc/lib/internationalization.lib.php @@ -1092,7 +1092,8 @@ function api_transliterate($string, $unknown = '?', $from_encoding = null) * @link http://php.net/manual/en/function.ord.php * Note the difference with the original funtion ord(): ord('') returns 0, api_ord('') returns 0xFFFD (unknown character). */ -function api_ord($character, $encoding = null) { +function api_ord($character, $encoding = 'UTF-8') +{ return Utf8::ord(api_utf8_encode($character, $encoding)); } diff --git a/main/install/configuration.dist.php b/main/install/configuration.dist.php index 228f81eee1..bd0437a40f 100755 --- a/main/install/configuration.dist.php +++ b/main/install/configuration.dist.php @@ -309,4 +309,13 @@ $_configuration['system_stable'] = NEW_VERSION_STABLE; // Hide LP time in reports. // $_configuration['hide_lp_time'] = false; // Hide rating elements in pages ("Courses catalog" & "Most Popular courses") -// $_configuration['hide_course_rating'] = false; \ No newline at end of file +// $_configuration['hide_course_rating'] = false; +// Customize password generation and verification +/*$_configuration['password_requirements'] = [ + 'min' => [ + 'lowercase' => 2, + 'uppercase' => 2, + 'numeric' => 2, + 'length' => 8 + ] +];*/