diff --git a/main/admin/configure_inscription.php b/main/admin/configure_inscription.php index faf736ab31..fdbd75e223 100755 --- a/main/admin/configure_inscription.php +++ b/main/admin/configure_inscription.php @@ -218,9 +218,7 @@ if ($display_all_form) { $form->addRule('pass1', get_lang('ThisFieldIsRequired'), 'required'); $form->addRule('pass2', get_lang('ThisFieldIsRequired'), 'required'); $form->addRule(array('pass1', 'pass2'), get_lang('PassTwo'), 'compare'); - if (CHECK_PASS_EASY_TO_FIND === true) { - $form->addRule('pass1', get_lang('PassTooEasy').': '.api_generate_password(), 'callback', 'api_check_password'); - } + $form->addPasswordRule('pass1'); // PHONE $form->addElement('text', 'phone', get_lang('Phone'), array('size' => 40, 'disabled' => 'disabled')); diff --git a/main/admin/user_add.php b/main/admin/user_add.php index deb7a2075e..00837f2a4d 100755 --- a/main/admin/user_add.php +++ b/main/admin/user_add.php @@ -210,6 +210,7 @@ $group[] = $form->createElement( ); $form->addGroup($group, 'password', get_lang('Password')); +$form->addPasswordRule('password', 'password'); $form->addGroupRule('password', get_lang('EnterPassword'), 'required', null, 1); if ($checkPass) { @@ -472,3 +473,5 @@ $tpl = new Template($tool_name); $tpl->assign('message', $message); $tpl->assign('content', $content); $tpl->display_one_col_template(); + + diff --git a/main/admin/user_edit.php b/main/admin/user_edit.php index 247e42202a..9c39057a0e 100755 --- a/main/admin/user_edit.php +++ b/main/admin/user_edit.php @@ -231,7 +231,9 @@ $group[] = $form->createElement( null, array('onkeydown' => 'javascript: password_switch_radio_button();') ); + $form->addGroup($group, 'password', null, null, false); +$form->addPasswordRule('password', 'password'); // Status $status = array(); diff --git a/main/auth/inscription.php b/main/auth/inscription.php index 72a789254f..f6f30135d4 100755 --- a/main/auth/inscription.php +++ b/main/auth/inscription.php @@ -178,15 +178,7 @@ if ($user_already_registered_show_terms === false) { $form->addRule('pass1', get_lang('ThisFieldIsRequired'), 'required'); $form->addRule('pass2', get_lang('ThisFieldIsRequired'), 'required'); $form->addRule(array('pass1', 'pass2'), get_lang('PassTwo'), 'compare'); - - if (CHECK_PASS_EASY_TO_FIND === true) { - $form->addRule( - 'pass1', - get_lang('PassTooEasy') . ': ' . api_generate_password(), - 'callback', - 'api_check_password' - ); - } + $form->addPasswordRule('pass1'); // PHONE if (in_array('phone', $allowedFields)) { diff --git a/main/auth/profile.php b/main/auth/profile.php index c6f9b2fff0..c8b9c3b787 100755 --- a/main/auth/profile.php +++ b/main/auth/profile.php @@ -316,9 +316,7 @@ if (is_platform_authentication() && $form->addElement('password', 'password2', get_lang('Confirmation'), array('size' => 40)); // user must enter identical password twice so we can prevent some user errors $form->addRule(array('password1', 'password2'), get_lang('PassTwo'), 'compare'); - if (CHECK_PASS_EASY_TO_FIND === true) { - $form->addRule('password1', get_lang('CurrentPasswordEmptyOrIncorrect'), 'callback', 'api_check_password'); - } + $form->addPasswordRule('password1'); } $extraField = new ExtraField('user'); diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index ae3bd4dd31..9757a0cfb7 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -2008,29 +2008,24 @@ function api_generate_password($length = 8) * 2. Only English letters (uppercase or lowercase, it doesn't matter) and digits are allowed. * 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. * Settings will change if the configuration value is set: password_requirements */ function api_check_password($password) { - $passwordRequirements = api_get_configuration_value('password_requirements'); + $passwordRequirements = Security::getPasswordRequirements(); - $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; - } + $minLength = $passwordRequirements['min']['length']; + $minNumbers = $passwordRequirements['min']['numeric']; + // Optional + $minLowerCase = $passwordRequirements['min']['lowercase']; + $minUpperCase = $passwordRequirements['min']['uppercase']; + + $minLetters = $minLowerCase + $minUpperCase; $passwordLength = api_strlen($password); - if ($passwordLength < $minLength) { - return false; - } + + $conditions = [ + 'min_length' => $passwordLength >= $minLength + ]; $digits = 0; $lowerCase = 0; @@ -2049,17 +2044,40 @@ function api_check_password($password) $digits++; } } + + // Min number of digits + $conditions['min_numeric'] = $digits >= $minNumbers; + + if (!empty($minUpperCase)) { + // Uppercase + $conditions['min_uppercase'] = $upperCase >= $minUpperCase; + } + + if (!empty($minLowerCase)) { + // Lowercase + $conditions['min_lowercase'] = $upperCase >= $minLowerCase; + } + + // Min letters $letters = $upperCase + $lowerCase; + $conditions['min_letters'] = $letters >= $minLetters; - if (!empty($passwordRequirements)) { - return ( - $upperCase >= $minUpperCase && - $lowerCase >= $minLowerCase && - $digits >= $minNumbers - ); + $isPasswordOk = true; + foreach ($conditions as $condition) { + if ($condition === false) { + $isPasswordOk = false; + break; + } + } + + if ($isPasswordOk === false) { + $output = get_lang('NewPasswordRequirementsNotMatched').'
'; + $output .= Security::getPasswordRequirementsToString($conditions); + + Display::addFlash(Display::return_message($output, 'warning', false)); } - return $letters >= $minLetters && $digits >= $minNumbers; + return $isPasswordOk; } /** diff --git a/main/inc/lib/formvalidator/FormValidator.class.php b/main/inc/lib/formvalidator/FormValidator.class.php index 93a708130e..2bdd92f622 100755 --- a/main/inc/lib/formvalidator/FormValidator.class.php +++ b/main/inc/lib/formvalidator/FormValidator.class.php @@ -1679,6 +1679,47 @@ EOT; }); "); } + + /** + * @param string $elementName + * @param string $groupName if element is inside a group + * @throws Exception + */ + public function addPasswordRule($elementName, $groupName = '') + { + // Constant defined in old config/profile.conf.php + if (CHECK_PASS_EASY_TO_FIND === true) { + $message = get_lang('PassTooEasy').': '.api_generate_password(); + + if (!empty($groupName)) { + $groupObj = $this->getElement($groupName); + + if ($groupObj instanceof HTML_QuickForm_group) { + $elementName = $groupObj->getElementName($elementName); + + if ($elementName === false) { + throw new Exception("The $groupName doesn't have the element $elementName"); + } + + $this->_rules[$elementName][] = array( + 'type' => 'callback', + 'format' => 'api_check_password', + 'message' => $message, + 'validation' => '', + 'reset' => false, + 'group' => $groupName + ); + } + } else { + $this->addRule( + $elementName, + $message, + 'callback', + 'api_check_password' + ); + } + } + } } /** diff --git a/main/inc/lib/security.lib.php b/main/inc/lib/security.lib.php index 6509200f47..b250d6b6b0 100755 --- a/main/inc/lib/security.lib.php +++ b/main/inc/lib/security.lib.php @@ -432,4 +432,58 @@ class Security } return $image_path; } + + /** + * Get password requirements + * It checks config value 'password_requirements' or uses the "classic" + * Chamilo password requirements. + * + * @return array + */ + public static function getPasswordRequirements() + { + // Default + $requirements = [ + 'min' => [ + 'lowercase' => 0, + 'uppercase' => 0, + 'numeric' => 2, + 'length' => 5 + ] + ]; + + $passwordRequirements = api_get_configuration_value('password_requirements'); + if (!empty($passwordRequirements)) { + $requirements = $passwordRequirements; + } + + return $requirements; + } + + /** + * Gets password requirements in the platform language using get_lang + * based in platform settings. See function 'self::getPasswordRequirements' + * @return string + */ + public static function getPasswordRequirementsToString($passedConditions = []) + { + $output = ''; + $setting = Security::getPasswordRequirements(); + foreach ($setting as $type => $rules) { + foreach ($rules as $rule => $parameter) { + if (empty($parameter)) { + continue; + } + $output .= sprintf( + get_lang( + 'NewPasswordRequirement'.ucfirst($type).'X'.ucfirst($rule) + ), + $parameter + ); + $output .= '
'; + } + } + + return $output; + } }