diff --git a/.gitignore b/.gitignore
index ab2cab23..923fe182 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,6 @@
/htdocs/vendor/
composer.lock
tests/.phpunit.result.cache
+htdocs/js/ppolicy.js
+htdocs/css/ppolicy.css
+templates/policy.tpl
diff --git a/composer.json b/composer.json
index 6968a43f..4a58b82c 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"require": {
- "ltb-project/ltb-common": "dev-main",
+ "ltb-project/ltb-common": "dev-36-add-password-policy",
"bjeavons/zxcvbn-php": "^1.0",
"twbs/bootstrap": "v5.3.3",
"defuse/php-encryption": "2.4.0",
@@ -22,7 +22,10 @@
"rm -rf htdocs/vendor/font-awesome",
"cp -R vendor/fortawesome/font-awesome htdocs/vendor/font-awesome",
"rm -rf htdocs/vendor/font-awesome/js htdocs/vendor/font-awesome/js-packages htdocs/vendor/font-awesome/less htdocs/vendor/font-awesome/metadata htdocs/vendor/font-awesome/otfs htdocs/vendor/font-awesome/scss htdocs/vendor/font-awesome/sprites htdocs/vendor/font-awesome/svgs",
- "rm -rf vendor/fortawesome/font-awesome"
+ "rm -rf vendor/fortawesome/font-awesome",
+ "cp -f vendor/ltb-project/ltb-common/src/ppolicy/html/policy.tpl templates/policy.tpl",
+ "cp -f vendor/ltb-project/ltb-common/src/ppolicy/js/ppolicy.js htdocs/js/ppolicy.js",
+ "cp -f vendor/ltb-project/ltb-common/src/ppolicy/css/ppolicy.css htdocs/css/ppolicy.css"
]
},
"require-dev": {
diff --git a/htdocs/change.php b/htdocs/change.php
index 6f2c518b..b7c92514 100644
--- a/htdocs/change.php
+++ b/htdocs/change.php
@@ -159,7 +159,7 @@
# Check password strength
#==============================================================================
if ( !$result ) {
- $result = check_password_strength( $newpassword, $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
+ $result = \Ltb\Ppolicy::check_password_strength( $newpassword, $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
}
#==============================================================================
diff --git a/htdocs/changecustompwdfield.php b/htdocs/changecustompwdfield.php
index 0de0718a..2e038a66 100644
--- a/htdocs/changecustompwdfield.php
+++ b/htdocs/changecustompwdfield.php
@@ -217,7 +217,7 @@ function set_default_value(&$variable, $defaultValue)
# Check password strength
#==============================================================================
if ( !$result ) {
- $result = check_password_strength( $newcustompwd, $password, $custompwdfield['pwd_policy_config'], $login, $entry_array, $change_custompwdfield );
+ $result = \Ltb\Ppolicy::check_password_strength( $newcustompwd, $password, $custompwdfield['pwd_policy_config'], $login, $entry_array, $change_custompwdfield );
}
#==============================================================================
diff --git a/htdocs/checkentropy.php b/htdocs/checkentropy.php
index 58cdcc61..cf237f0f 100644
--- a/htdocs/checkentropy.php
+++ b/htdocs/checkentropy.php
@@ -1,67 +1,15 @@
int, "message" => "msg" } */
-
-function checkEntropyJSON($password_base64)
-{
- $response_params = array();
- $zxcvbn = new Zxcvbn();
-
- if( ! isset($password_base64) || empty($password_base64))
- {
- error_log("checkEntropy: missing parameter password");
- $response_params["level"] = -1;
- $response_params["message"] = "missing parameter password";
- print json_encode($response_params);
- exit(1);
- }
-
- $p = base64_decode($password_base64);
- // force encoding to utf8, as iso-8859-1 is not supported by zxcvbn
- $password = mb_convert_encoding($p, 'UTF-8', 'ISO-8859-1');
- error_log("checkEntropy: password taken from submitted form");
-
- $entropy = $zxcvbn->passwordStrength("$password");
-
- $response_params["level"] = strval($entropy["score"]);
- $response_params["message"] = $entropy['feedback']['warning'] ? strval($entropy['feedback']['warning']) : "";
-
- error_log("checkEntropy: level " . $response_params["level"] . " msg: " . $response_params["message"]);
-
- print json_encode($response_params);
- exit(0);
-}
// new password sent in the url, base64 encoded
$newpass = htmlspecialchars($_POST["password"]);
-checkEntropyJSON($newpass);
+$entropy_response = \Ltb\Ppolicy::checkEntropyJSON($newpass);
+if ($debug) {
+ error_log("checkEntropy: ".$entropy_response);
+}
+
+print $entropy_response;
+exit(0);
?>
diff --git a/htdocs/css/self-service-password.css b/htdocs/css/self-service-password.css
index e5cbeb75..79bbffb1 100644
--- a/htdocs/css/self-service-password.css
+++ b/htdocs/css/self-service-password.css
@@ -65,38 +65,3 @@ textarea#sshkey {
}
}
-
-/* password entropy customization*/
-#entropybar>div {
- width: 0%;
- /* Adjust with JavaScript */
-}
-
-#entropybar>div.levelErr {
- width: 0%;
-}
-
-#entropybar>div.level0 {
- width: 20%;
-}
-
-#entropybar>div.level1 {
- width: 40%;
-}
-
-#entropybar>div.level2 {
- width: 60%;
-}
-
-#entropybar>div.level3 {
- width: 80%;
-}
-
-#entropybar>div.level4 {
- width: 100%;
-}
-
-.entropyHidden {
- display: none;
-}
-
diff --git a/htdocs/index.php b/htdocs/index.php
index cfbd40d2..13e72282 100644
--- a/htdocs/index.php
+++ b/htdocs/index.php
@@ -315,64 +315,13 @@
if (isset($login)) { $smarty->assign('login', $login); }
if (isset($token)) { $smarty->assign('token', $token); }
if (isset($use_captcha)) { $smarty->assign('use_captcha', $use_captcha); }
-// TODO : Make it clean function show_policy - START
-if (isset($pwd_show_policy_pos)) {
- $smarty->assign('pwd_show_policy_pos', $pwd_show_policy_pos);
- $smarty->assign('pwd_show_policy', $pwd_show_policy);
- $smarty->assign('pwd_show_policy_onerror', true);
- if ( $pwd_show_policy === "onerror" ) {
- if ( !preg_match( "/tooshort|toobig|minlower|minupper|mindigit|minspecial|forbiddenchars|sameasold|notcomplex|sameaslogin|pwned|specialatends/" , $result) ) {
- $smarty->assign('pwd_show_policy_onerror', false);
- } else {
- $smarty->assign('pwd_show_policy_onerror', true);
- }
- }
- if (isset($pwd_min_length)) { $smarty->assign('pwd_min_length', $pwd_min_length); }
- if (isset($pwd_max_length)) { $smarty->assign('pwd_max_length', $pwd_max_length); }
- if (isset($pwd_min_lower)) { $smarty->assign('pwd_min_lower', $pwd_min_lower); }
- if (isset($pwd_min_upper)) { $smarty->assign('pwd_min_upper', $pwd_min_upper); }
- if (isset($pwd_min_digit)) { $smarty->assign('pwd_min_digit', $pwd_min_digit); }
- if (isset($pwd_min_special)) { $smarty->assign('pwd_min_special', $pwd_min_special); }
- if (isset($pwd_complexity)) { $smarty->assign('pwd_complexity', $pwd_complexity); }
- if (isset($pwd_diff_last_min_chars)) { $smarty->assign('pwd_diff_last_min_chars', $pwd_diff_last_min_chars); }
- if (isset($pwd_forbidden_chars)) { $smarty->assign('pwd_forbidden_chars', $pwd_forbidden_chars); }
- if (isset($pwd_no_reuse)) { $smarty->assign('pwd_no_reuse', $pwd_no_reuse); }
- if (isset($pwd_diff_login)) { $smarty->assign('pwd_diff_login', $pwd_diff_login); }
- if (isset($pwd_display_entropy)) { $smarty->assign('pwd_display_entropy', $pwd_display_entropy); }
- if (isset($pwd_check_entropy)) { $smarty->assign('pwd_check_entropy', $pwd_check_entropy); }
- if (isset($pwd_min_entropy)) { $smarty->assign('pwd_min_entropy', $pwd_min_entropy); }
- if (isset($use_pwnedpasswords)) { $smarty->assign('use_pwnedpasswords', $use_pwnedpasswords); }
- if (isset($pwd_no_special_at_ends)) { $smarty->assign('pwd_no_special_at_ends', $pwd_no_special_at_ends); }
-
- // send policy to a JSON object usable in javascript (window.policy.[parameter])
- $smarty->assign('json_policy', base64_encode(json_encode(
- array(
- "pwd_min_length" => $pwd_min_length,
- "pwd_max_length" => $pwd_max_length,
- "pwd_min_lower" => $pwd_min_lower,
- "pwd_min_upper" => $pwd_min_upper,
- "pwd_min_digit" => $pwd_min_digit,
- "pwd_min_special" => $pwd_min_special,
- "pwd_complexity" => $pwd_complexity,
- "pwd_diff_last_min_chars" => $pwd_diff_last_min_chars,
- "pwd_forbidden_chars" => $pwd_forbidden_chars,
- "pwd_no_reuse" => $pwd_no_reuse,
- "pwd_diff_login" => $pwd_diff_login,
- "pwd_display_entropy" => $pwd_display_entropy,
- "pwd_check_entropy" => $pwd_check_entropy,
- "pwd_min_entropy" => $pwd_min_entropy,
- "use_pwnedpasswords" => $use_pwnedpasswords,
- "pwd_no_special_at_ends" => $pwd_no_special_at_ends,
- "pwd_special_chars" => $pwd_special_chars
- )
- )));
-}
+
+\Ltb\Ppolicy::smarty_assign_ppolicy($smarty, $pwd_show_policy_pos, $pwd_show_policy, $result, $pwd_policy_config);
if (isset($custompwdindex)) {
$smarty->assign('custompwdindex', $custompwdindex);
if (isset($change_custompwdfield[$custompwdindex]['msg_passwordchangedextramessage'])) { $smarty->assign('msg_passwordchangedextramessage', $change_custompwdfield[$custompwdindex]['msg_passwordchangedextramessage']); }
}
-// TODO : Make it clean function show_policy - END
if (isset($smsdisplay)) { $smarty->assign('smsdisplay', $smsdisplay); }
// TODO : Make it clean $prehook_return/$posthook_return - START
if (isset($prehook_return)) {
diff --git a/htdocs/js/ppolicy.js b/htdocs/js/ppolicy.js
deleted file mode 100644
index aaabc367..00000000
--- a/htdocs/js/ppolicy.js
+++ /dev/null
@@ -1,376 +0,0 @@
-(function() {
- var barWidth, bootstrapClasses, displayEntropyBar, displayEntropyBarMsg, ppolicyResults;
-
- ppolicyResults = {};
-
- bootstrapClasses = new Map([["Err", "bg-danger"], ["0", "bg-danger"], ["1", "bg-warning"], ["2", "bg-info"], ["3", "bg-primary"], ["4", "bg-success"]]);
-
- barWidth = new Map([["Err", "0"], ["0", "20"], ["1", "40"], ["2", "60"], ["3", "80"], ["4", "100"]]);
-
- json_policy = $("#json-policy").data('policy');
- var local_policy = JSON.parse(atob(json_policy));
-
- displayEntropyBar = function(level) {
- $("#entropybar div").removeClass();
- $("#entropybar div").addClass('progress-bar');
- $("#entropybar div").width(barWidth.get(level) + '%');
- $("#entropybar div").addClass(bootstrapClasses.get(level));
- return $("#entropybar div").html(barWidth.get(level) + '%');
- };
-
- displayEntropyBarMsg = function(msg) {
- $("#entropybar-msg").html(msg);
- if (msg.length === 0) {
- return $("#entropybar-msg").addClass("entropyHidden");
- } else {
- return $("#entropybar-msg").removeClass("entropyHidden");
- }
- };
-
- setResult = function(field, result) {
- var ref, ref1;
- ppolicyResults[field] = result;
- $("#" + field).removeClass('fa-times fa-check fa-spinner fa-pulse fa-info-circle fa-question-circle text-danger text-success text-info text-secondary');
- $("#" + field).attr('role', 'status');
- switch (result) {
- case "good":
- $("#" + field).addClass('fa-check text-success');
- break;
- case "bad":
- $("#" + field).addClass('fa-times text-danger');
- $("#" + field).attr('role', 'alert');
- break;
- case "unknown":
- $("#" + field).addClass('fa-question-circle text-secondary');
- break;
- case "waiting":
- $("#" + field).addClass('fa-spinner fa-pulse text-secondary');
- break;
- case "info":
- $("#" + field).addClass('fa-info-circle text-info');
- }
- if (Object.values(ppolicyResults).every((function(_this) {
- return function(value) {
- return value === "good" || value === "info";
- };
- })(this))) {
- $('.ppolicy').removeClass('border-danger').addClass('border-success');
- return (ref = $('#newpassword').get(0)) != null ? ref.setCustomValidity('') : void 0;
- } else {
- $('.ppolicy').removeClass('border-success').addClass('border-danger');
- return (ref1 = $('#newpassword').get(0)) != null ? ref1.setCustomValidity("Insufficient quality") : void 0;
- }
- };
-
- similar_text = function(first, second, percent) {
- // discuss at: https://locutus.io/php/similar_text/
- // original by: RafaĆ Kukawski (https://blog.kukawski.pl)
- // bugfixed by: Chris McMacken
- // bugfixed by: Jarkko Rantavuori original by findings in stackoverflow (https://stackoverflow.com/questions/14136349/how-does-similar-text-work)
- // improved by: Markus Padourek (taken from https://www.kevinhq.com/2012/06/php-similartext-function-in-javascript_16.html)
- // MIT licenses
- // example 1: similar_text('Hello World!', 'Hello locutus!')
- // returns 1: 8
- // example 2: similar_text('Hello World!', null)
- // returns 2: 0
-
- if (first === null ||
- second === null ||
- typeof first === 'undefined' ||
- typeof second === 'undefined') {
- return 0
- }
-
- first += ''
- second += ''
-
- let pos1 = 0
- let pos2 = 0
- let max = 0
- const firstLength = first.length
- const secondLength = second.length
- let p
- let q
- let l
- let sum
-
- for (p = 0; p < firstLength; p++) {
- for (q = 0; q < secondLength; q++) {
- for (l = 0; (p + l < firstLength) && (q + l < secondLength) && (first.charAt(p + l) === second.charAt(q + l)); l++) {
- // @todo: ^-- break up this crazy for loop and put the logic in its body
- }
- if (l > max) {
- max = l
- pos1 = p
- pos2 = q
- }
- }
- }
-
- sum = max
-
- if (sum) {
- if (pos1 && pos2) {
- sum += similar_text(first.substr(0, pos1), second.substr(0, pos2))
- }
-
- if ((pos1 + max < firstLength) && (pos2 + max < secondLength)) {
- sum += similar_text(
- first.substr(pos1 + max, firstLength - pos1 - max),
- second.substr(pos2 + max,
- secondLength - pos2 - max))
- }
- }
-
- if (!percent) {
- return sum
- }
-
- return (sum * 200) / (firstLength + secondLength)
- }
-
-
- // Generic feature for checkpassword action
- // check all local policy criteria one by one and display an appropriate button for each
- $(document).on('checkpassword', function(event, context) {
- var digit, evType, hasforbidden, i, len, lower, numspechar, password, report, setResult, upper;
- password = context.password;
- evType = context.evType;
- setResult = context.setResult;
- report = function(result, id) {
- if (result) {
- return setResult(id, "good");
- } else {
- return setResult(id, "bad");
- }
- };
-
- removePPolicyCriteria = function(criteria, feedback) {
- // first consider the criteria as fullfilled
- report( true , feedback);
- // remove criteria from the list of ppolicy checks
- delete local_policy[criteria];
- // remove the
tag parent to given feedback
- $( "#" + feedback ).parent().remove();
- };
-
-
- // Criteria checks
- if (local_policy.pwd_min_length > 0) {
- report(password.length >= local_policy.pwd_min_length, 'ppolicy-pwd_min_length-feedback');
- }
-
- if (local_policy.pwd_max_length > 0) {
- report(password.length <= local_policy.pwd_max_length, 'ppolicy-pwd_max_length-feedback');
- }
-
- if (local_policy.pwd_min_upper > 0) {
- upper = password.match(/[A-Z]/g);
- report(upper && upper.length >= local_policy.pwd_min_upper, 'ppolicy-pwd_min_upper-feedback');
- }
-
- if (local_policy.pwd_min_lower > 0) {
- lower = password.match(/[a-z]/g);
- report(lower && lower.length >= local_policy.pwd_min_lower, 'ppolicy-pwd_min_lower-feedback');
- }
-
- if (local_policy.pwd_min_digit > 0) {
- digit = password.match(/[0-9]/g);
- report(digit && digit.length >= local_policy.pwd_min_digit, 'ppolicy-pwd_min_digit-feedback');
- }
-
- if (local_policy.pwd_no_reuse && local_policy.pwd_no_reuse == true) {
- if( $( "#oldpassword" ).length )
- {
- oldpassword = $( "#oldpassword" ).val();
- report( password != oldpassword , 'ppolicy-pwd_no_reuse-feedback');
- }
- else
- {
- removePPolicyCriteria("pwd_no_reuse", 'ppolicy-pwd_no_reuse-feedback');
- }
- }
-
- if (local_policy.pwd_diff_login && local_policy.pwd_diff_login == true) {
- if( $( "#login" ).length )
- {
- login = $( "#login" ).val();
- report( password != login, 'ppolicy-pwd_diff_login-feedback');
- }
- else
- {
- report( true , 'ppolicy-pwd_diff_login-feedback');
- }
- }
-
- if (local_policy.pwd_diff_last_min_chars > 0) {
- if( $( "#oldpassword" ).length )
- {
- minDiffChars = local_policy.pwd_diff_last_min_chars;
- oldpassword = $( "#oldpassword" ).val();
-
- similarities = similar_text(oldpassword, password);
- check_len = oldpassword.length < password.length ? oldpassword.length : password.length;
- new_chars = check_len - similarities;
- report( new_chars > minDiffChars , 'ppolicy-pwd_diff_last_min_chars-feedback');
- }
- else
- {
- removePPolicyCriteria("pwd_diff_last_min_chars", 'ppolicy-pwd_diff_last_min_chars-feedback');
- }
- }
-
- if (local_policy.pwd_forbidden_chars) {
- forbiddenChars = local_policy.pwd_forbidden_chars;
- forbidden = false;
- i = 0;
- while (i < password.length) {
- if (forbiddenChars.indexOf(password.charAt(i)) != -1) {
- forbidden = true;
- }
- i++;
- }
- report( !forbidden, 'ppolicy-pwd_forbidden_chars-feedback' );
- }
-
- if (local_policy.pwd_min_special > 0 && local_policy.pwd_special_chars) {
- numspechar = 0;
- var re = new RegExp("["+local_policy.pwd_special_chars+"]","");
- i = 0;
- while (i < password.length) {
- if (password.charAt(i).match(re)) {
- numspechar++;
- }
- i++;
- }
- report(numspechar >= local_policy.pwd_min_special, 'ppolicy-pwd_min_special-feedback');
- }
-
- if ( local_policy.pwd_no_special_at_ends &&
- local_policy.pwd_no_special_at_ends == true &&
- local_policy.pwd_special_chars ) {
- var re_start = new RegExp("^["+local_policy.pwd_special_chars+"]","");
- var re_end = new RegExp("["+local_policy.pwd_special_chars+"]$","");
- report( ( !password.match(re_start) && !password.match(re_end) ) , 'ppolicy-pwd_no_special_at_ends-feedback');
- }
-
- if ( local_policy.pwd_complexity) {
- complexity = 0;
- if (local_policy.pwd_special_chars) {
- var re = new RegExp("["+local_policy.pwd_special_chars+"]","");
- if( password.match(re) ){
- complexity++;
- }
- }
- if( password.match(/[A-Z]/g) ){
- complexity++;
- }
- if( password.match(/[a-z]/g) ){
- complexity++;
- }
- if( password.match(/[0-9]/g) ){
- complexity++;
- }
- report( complexity >= local_policy.pwd_complexity, 'ppolicy-pwd_complexity-feedback');
- }
-
-
- if ( local_policy.use_pwnedpasswords) {
- setResult('ppolicy-use_pwnedpasswords-feedback', "info");
- }
-
- });
-
-
-
- // Specific feature for checkentropy action
- $(document).on('checkpassword', function(event, context) {
- var entropyrequired, entropyrequiredlevel, evType, newpasswordVal, password, setResult;
- password = context.password;
- evType = context.evType;
- setResult = context.setResult;
- if ($('#ppolicy-checkentropy-feedback').length > 0) {
- newpasswordVal = $("#newpassword").val();
- entropyrequired = $("span[trspan='checkentropyLabel']").attr("data-checkentropy_required");
- entropyrequiredlevel = $("span[trspan='checkentropyLabel']").attr("data-checkentropy_required_level");
- if (newpasswordVal.length === 0) {
- displayEntropyBar("Err");
- displayEntropyBarMsg("");
- setResult('ppolicy-checkentropy-feedback', "unknown");
- }
- if (newpasswordVal.length > 0) {
- return $.ajax({
- dataType: "json",
- url: location.pathname + "?action=checkentropy",
- method: "POST",
- data: { "password": btoa(newpasswordVal) },
- context: document.body,
- success: function(data) {
- var level, msg;
- level = data.level;
- msg = data.message;
- if (level !== void 0) {
- if (parseInt(level) >= 0 && parseInt(level) <= 4) {
- displayEntropyBar(level);
- displayEntropyBarMsg(msg);
- if (entropyrequired === "1" && entropyrequiredlevel.length > 0) {
- if (parseInt(level) >= parseInt(entropyrequiredlevel)) {
- setResult('ppolicy-checkentropy-feedback', "good");
- } else {
- setResult('ppolicy-checkentropy-feedback', "bad");
- }
- }
- if (entropyrequired !== "1") {
- return setResult('ppolicy-checkentropy-feedback', "good");
- }
- } else if (parseInt(level) === -1) {
- displayEntropyBar(level);
- displayEntropyBarMsg(msg);
- return setResult('ppolicy-checkentropy-feedback', "bad");
- } else {
- displayEntropyBar(level);
- displayEntropyBarMsg(msg);
- return setResult('ppolicy-checkentropy-feedback', "unknown");
- }
- }
- },
- error: function(j, status, err) {
- var res;
- if (err) {
- console.log('checkentropy: frontend error: ', err);
- }
- if (j) {
- res = JSON.parse(j.responseText);
- }
- if (res && res.error) {
- return console.log('checkentropy: returned error: ', res);
- }
- }
- });
- }
- }
- });
-
-
-
- checkpassword = function(password, evType) {
- var e, info;
- e = jQuery.Event("checkpassword");
- info = {
- password: password,
- evType: evType,
- setResult: setResult
- };
- return $(document).trigger(e, info);
- };
- if ( (local_policy != null) && $('#newpassword').length) {
- checkpassword('');
- $('#newpassword').keyup(function(e) {
- checkpassword(e.target.value);
- });
- $('#newpassword').focusout(function(e) {
- checkpassword(e.target.value, "focusout");
- });
- }
-
-}).call(this);
diff --git a/htdocs/resetbyquestions.php b/htdocs/resetbyquestions.php
index ddfcd4a0..92fcc1c1 100644
--- a/htdocs/resetbyquestions.php
+++ b/htdocs/resetbyquestions.php
@@ -220,7 +220,7 @@
# Check password strength
if ( !$result ) {
- $result = check_password_strength( $newpassword, "", $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
+ $result = \Ltb\Ppolicy::check_password_strength( $newpassword, "", $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
}
# Change password
diff --git a/htdocs/resetbytoken.php b/htdocs/resetbytoken.php
index fe50a6ed..7910844d 100644
--- a/htdocs/resetbytoken.php
+++ b/htdocs/resetbytoken.php
@@ -152,7 +152,7 @@
# Check password strength
if ( !$result ) {
$entry_array = ldap_get_attributes($ldap, $entry);
- $result = check_password_strength( $newpassword, "", $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
+ $result = \Ltb\Ppolicy::check_password_strength( $newpassword, "", $pwd_policy_config, $login, $entry_array, $change_custompwdfield );
}
# Change password
diff --git a/lib/functions.inc.php b/lib/functions.inc.php
index e430c822..c1778ccc 100644
--- a/lib/functions.inc.php
+++ b/lib/functions.inc.php
@@ -33,16 +33,6 @@
*/
require_once __DIR__ . '/../vendor/autoload.php';
-use ZxcvbnPhp\Zxcvbn;
-
-try{
- $zxcvbn = new Zxcvbn();
- error_log("Module Zxcvbn successfully loaded");
-}
-catch(Throwable $e){
- error_log("Could not load Zxcvbn module: ".$e);
- exit(1);
-}
# Generate SMS token
function generate_sms_token( $sms_token_length ) {
@@ -82,180 +72,6 @@ function get_fa_class( $msg) {
}
-# Check password strength
-# @param array entry_array ldap entry ( ie not resource or LDAP\Result )
-# @return result code
-function check_password_strength( $password, $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) {
- extract( $pwd_policy_config );
-
- $result = "";
-
- $length = mb_strlen($password, mb_detect_encoding($password));
- preg_match_all("/[a-z]/", $password, $lower_res);
- $lower = count( $lower_res[0] );
- preg_match_all("/[A-Z]/", $password, $upper_res);
- $upper = count( $upper_res[0] );
- preg_match_all("/[0-9]/", $password, $digit_res);
- $digit = count( $digit_res[0] );
-
- $special = 0;
- $special_at_ends = false;
- if ( isset($pwd_special_chars) && !empty($pwd_special_chars) ) {
- preg_match_all("/[$pwd_special_chars]/", $password, $special_res);
- $special = count( $special_res[0] );
- if ( $pwd_no_special_at_ends ) {
- $special_at_ends = preg_match("/(^[$pwd_special_chars]|[$pwd_special_chars]$)/", $password, $special_res);
- }
- }
-
- $forbidden = 0;
- if ( isset($pwd_forbidden_chars) && !empty($pwd_forbidden_chars) ) {
- preg_match_all("/[$pwd_forbidden_chars]/", $password, $forbidden_res);
- $forbidden = count( $forbidden_res[0] );
- }
-
- # Complexity: checks for lower, upper, special, digits
- if ( $pwd_complexity ) {
- $complex = 0;
- if ( $special > 0 ) { $complex++; }
- if ( $digit > 0 ) { $complex++; }
- if ( $lower > 0 ) { $complex++; }
- if ( $upper > 0 ) { $complex++; }
- if ( $complex < $pwd_complexity ) { $result="notcomplex"; }
- }
-
- # Minimal lenght
- if ( $pwd_min_length and $length < $pwd_min_length ) { $result="tooshort"; }
-
- # Maximal lenght
- if ( $pwd_max_length and $length > $pwd_max_length ) { $result="toobig"; }
-
- # Minimal lower chars
- if ( $pwd_min_lower and $lower < $pwd_min_lower ) { $result="minlower"; }
-
- # Minimal upper chars
- if ( $pwd_min_upper and $upper < $pwd_min_upper ) { $result="minupper"; }
-
- # Minimal digit chars
- if ( $pwd_min_digit and $digit < $pwd_min_digit ) { $result="mindigit"; }
-
- # Minimal special chars
- if ( $pwd_min_special and $special < $pwd_min_special ) { $result="minspecial"; }
-
- # Forbidden chars
- if ( $forbidden > 0 ) { $result="forbiddenchars"; }
-
- # Special chars at beginning or end
- if ( $special_at_ends > 0 && $special == 1 ) { $result="specialatends"; }
-
- # Same as old password?
- if ( $pwd_no_reuse and $password === $oldpassword ) { $result="sameasold"; }
-
- # Same as login?
- if ( $pwd_diff_login and $password === $login ) { $result="sameaslogin"; }
-
- if ( $pwd_diff_last_min_chars > 0 and strlen($oldpassword) > 0 ) {
- $similarities = similar_text($oldpassword, $password);
- $check_len = strlen($oldpassword) < strlen($password) ? strlen($oldpassword) : strlen($password);
- $new_chars = $check_len - $similarities;
- if ($new_chars <= $pwd_diff_last_min_chars) { $result = "diffminchars"; }
- }
-
- # Contains forbidden words?
- if ( !empty($pwd_forbidden_words) ) {
- foreach( $pwd_forbidden_words as $disallowed ) {
- if( stripos($password, $disallowed) !== false ) {
- $result="forbiddenwords";
- break;
- }
- }
- }
-
- # Contains values from forbidden ldap fields?
- if ( !empty($pwd_forbidden_ldap_fields) ) {
- foreach ( $pwd_forbidden_ldap_fields as $field ) {
- # if entry does not hold requested attribute, continue
- if ( array_key_exists($field,$entry_array) )
- {
- $values = $entry_array[$field];
- if (!is_array($values)) {
- $values = array($values);
- }
- foreach ($values as $key => $value) {
- if ($key === 'count') {
- continue;
- }
- if (stripos($password, $value) !== false) {
- $result = "forbiddenldapfields";
- break 2;
- }
- }
- }
- }
- }
-
- # ensure that the new password is different from any other custom password field marked as unique
- foreach ( $change_custompwdfield as $custompwdfield) {
- if (isset($custompwdfield['pwd_policy_config']['pwd_unique_across_custom_password_fields']) &&
- $custompwdfield['pwd_policy_config']['pwd_unique_across_custom_password_fields']) {
- if (array_key_exists($custompwdfield['attribute'], $entry_array)) {
- if ($custompwdfield['hash'] == 'auto') {
- $matches = [];
- if ( preg_match( '/^\{(\w+)\}/', $entry_array[$custompwdfield['attribute']][0], $matches ) ) {
- $hash_for_custom_pwd = strtoupper($matches[1]);
- }
- } else {
- $hash_for_custom_pwd = $custompwdfield['hash'];
- }
- if ( \Ltb\Password::check_password($password, $entry_array[$custompwdfield['attribute']][0], $hash_for_custom_pwd) ) {
- $result = "sameascustompwd";
- }
- }
- }
- }
-
- # pwned?
- if ($use_pwnedpasswords and version_compare(PHP_VERSION, '7.2.5') >= 0) {
- $pwned_passwords = new PwnedPasswords\PwnedPasswords;
- $insecure = $pwned_passwords->isPwned($password);
- if ($insecure) { $result="pwned"; }
- }
-
-
- # check entropy
- $zxcvbn = new Zxcvbn();
- if( isset($pwd_check_entropy) && $pwd_check_entropy == true )
- {
- if( isset($pwd_min_entropy) && is_int($pwd_min_entropy) )
- {
- // force encoding to utf8, as iso-8859-1 is not supported by zxcvbn
- //$password = mb_convert_encoding($p, 'UTF-8', 'ISO-8859-1');
- error_log("checkEntropy: password taken directly");
- $entropy = $zxcvbn->passwordStrength("$password");
- $entropy_level = intval($entropy["score"]);
- $entropy_message = $entropy['feedback']['warning'] ? strval($entropy['feedback']['warning']) : "";
- error_log( "checkEntropy: level $entropy_level msg: $entropy_message" );
- if( is_int($entropy_level) && $entropy_level >= $pwd_min_entropy )
- {
- ; // password etropy check ok
- }
- else
- {
- error_log("checkEntropy: insufficient entropy: level = $entropy_level but minimal required = $pwd_min_entropy");
- $result="insufficiententropy";
- }
- }
- else
- {
- error_log("checkEntropy: missing required parameter pwd_min_entropy");
- $result="insufficiententropy";
- }
-
- }
-
- return $result;
-}
-
# Change password
# @return result code
function change_password( $ldapInstance, $dn, $password, $ad_mode, $ad_options, $samba_mode, $samba_options, $shadow_options, $hash, $hash_options, $who_change_password, $oldpassword, $use_exop_passwd, $use_ppolicy_control, $custom_pwd_field_mode, $custom_pwd_attribute ) {
diff --git a/templates/footer.tpl b/templates/footer.tpl
index 9af0a929..76d764fe 100644
--- a/templates/footer.tpl
+++ b/templates/footer.tpl
@@ -1,8 +1,8 @@
{if $display_footer}
-
{/if}
+ssp
diff --git a/templates/header.tpl b/templates/header.tpl
index 294ef3c8..d52adcd7 100644
--- a/templates/header.tpl
+++ b/templates/header.tpl
@@ -11,6 +11,7 @@
+
{if $custom_css}
{/if}
diff --git a/templates/policy.tpl b/templates/policy.tpl
deleted file mode 100644
index ebb484ba..00000000
--- a/templates/policy.tpl
+++ /dev/null
@@ -1,129 +0,0 @@
-{if $pwd_show_policy === "onerror" and !$pwd_show_policy_onerror }
-{else}
-
- {$msg_policy|unescape: "html" nofilter}
-
-
-
- {if $pwd_min_length }
- -
-
- {$msg_policyminlength|unescape: "html" nofilter} {$pwd_min_length}
-
- {/if}
-
-
- {if $pwd_max_length }
- -
-
- {$msg_policymaxlength|unescape: "html" nofilter} {$pwd_max_length}
-
- {/if}
-
-
- {if $pwd_min_lower }
- -
-
- {$msg_policyminlower|unescape: "html" nofilter} {$pwd_min_lower}
-
- {/if}
-
-
- {if $pwd_min_upper }
- -
-
- {$msg_policyminupper|unescape: "html" nofilter} {$pwd_min_upper}
-
- {/if}
-
-
- {if $pwd_min_digit }
- -
-
- {$msg_policymindigit|unescape: "html" nofilter} {$pwd_min_digit}
-
- {/if}
-
-
- {if $pwd_min_special }
- -
-
- {$msg_policyminspecial|unescape: "html" nofilter} {$pwd_min_special}
-
- {/if}
-
-
- {if $pwd_complexity }
- -
-
- {$msg_policycomplex|unescape: "html" nofilter} {$pwd_complexity}
-
- {/if}
-
-
- {if $pwd_forbidden_chars }
- -
-
- {$msg_policyforbiddenchars|unescape: "html" nofilter} {$pwd_forbidden_chars}
-
- {/if}
-
-
- {if $pwd_diff_last_min_chars }
- -
-
- {$msg_policydiffminchars|unescape: "html" nofilter} {$pwd_diff_last_min_chars}
-
- {/if}
-
-
- {if $pwd_no_reuse }
- -
-
- {$msg_policynoreuse|unescape: "html" nofilter}
-
- {/if}
-
-
- {if $pwd_diff_login }
- -
-
- {$msg_policydifflogin|unescape: "html" nofilter}
-
- {/if}
-
-
- {if $use_pwnedpasswords }
- -
-
- {$msg_policypwned|unescape: "html" nofilter}
-
- {/if}
-
-
- {if $pwd_no_special_at_ends }
- -
-
- {$msg_policyspecialatends|unescape: "html" nofilter}
-
- {/if}
-
-
- {if $pwd_display_entropy }
- -
-
- {if $pwd_check_entropy }
- {$msg_policyentropy|unescape: "html" nofilter}
- {else}
- {$msg_policyentropy|unescape: "html" nofilter}
- {/if}
-
-
-
- {/if}
-
-
-
-{/if}
diff --git a/tests/CheckPasswordTest.php b/tests/CheckPasswordTest.php
deleted file mode 100644
index c7909cf8..00000000
--- a/tests/CheckPasswordTest.php
+++ /dev/null
@@ -1,229 +0,0 @@
- true,
- "pwd_min_length" => 6,
- "pwd_max_length" => 12,
- "pwd_min_lower" => 1,
- "pwd_min_upper" => 1,
- "pwd_min_digit" => 1,
- "pwd_min_special" => 1,
- "pwd_special_chars" => "^a-zA-Z0-9",
- "pwd_forbidden_chars" => "@",
- "pwd_no_reuse" => true,
- "pwd_diff_last_min_chars" => 0,
- "pwd_diff_login" => true,
- "pwd_complexity" => 0,
- "use_pwnedpasswords" => false,
- "pwd_no_special_at_ends" => false,
- "pwd_forbidden_words" => array(),
- "pwd_forbidden_ldap_fields"=> array(),
- );
-
- $login = "coudot";
- $oldpassword = "secret";
- $entry_array = array('cn' => array('common name'), 'sn' => array('surname'), 'customPasswordField' => array("{SSHA}7JWaNGUygodHyWt+DwPpOuYMDdKYJQQX"));
- $change_custompwdfield = array(
- array(
- 'pwd_policy_config' => array(
- 'pwd_no_reuse' => true,
- 'pwd_unique_across_custom_password_fields' => true
- ),
- 'attribute' => 'customPasswordField',
- 'hash' => "auto"
- )
- );
- $change_custompwdfield2 = array(
- array(
- 'pwd_policy_config' => array(
- 'pwd_no_reuse' => true,
- 'pwd_unique_across_custom_password_fields' => true
- ),
- 'attribute' => 'customPasswordField',
- 'hash' => "SSHA"
- )
- );
-
- $this->assertEquals("sameaslogin", check_password_strength( "coudot", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("sameasold", check_password_strength( "secret", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenchars", check_password_strength( "p@ssword", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("minspecial", check_password_strength( "password", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("mindigit", check_password_strength( "!password", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("minupper", check_password_strength( "!1password", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("minlower", check_password_strength( "!1PASSWORD", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("toobig", check_password_strength( "!1verylongPassword", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("tooshort", check_password_strength( "!1Pa", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("sameascustompwd", check_password_strength( "!TestMe123!", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("sameascustompwd", check_password_strength( "!TestMe123!", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield2 ) );
-
-
- $pwd_policy_config = array(
- "pwd_show_policy" => true,
- "pwd_min_length" => 6,
- "pwd_max_length" => 12,
- "pwd_min_lower" => 0,
- "pwd_min_upper" => 0,
- "pwd_min_digit" => 0,
- "pwd_min_special" => 0,
- "pwd_special_chars" => "^a-zA-Z0-9",
- "pwd_forbidden_chars" => "@",
- "pwd_no_reuse" => true,
- "pwd_diff_last_min_chars" => 3,
- "pwd_diff_login" => true,
- "pwd_complexity" => 3,
- "use_pwnedpasswords" => false,
- "pwd_no_special_at_ends" => true,
- "pwd_forbidden_words" => array('companyname', 'trademark'),
- "pwd_forbidden_ldap_fields"=> array('cn', 'sn'),
- );
-
- $this->assertEquals("notcomplex", check_password_strength( "simple", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("specialatends", check_password_strength( "!simple", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("specialatends", check_password_strength( "simple?", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenwords", check_password_strength( "companyname", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenwords", check_password_strength( "trademark", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenwords", check_password_strength( "working at companyname is fun", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenldapfields", check_password_strength( "common name", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("forbiddenldapfields", check_password_strength( "my surname", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("diffminchars", check_password_strength( "C0mplex", "C0mplexC0mplex", $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("", check_password_strength( "C0mplex", "", $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("", check_password_strength( "C0mplex", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("", check_password_strength( "C0!mplex", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- $this->assertEquals("", check_password_strength( "%C0!mplex", $oldpassword, $pwd_policy_config, $login, $entry_array, $change_custompwdfield ) );
- }
-
- /**
- * Test check_password_strength function with pwned passwords
- */
- public function testCheckPasswordStrengthPwnedPasswords()
- {
-
- # Load functions
- require_once __DIR__ . '/../lib/functions.inc.php';
-
- $login = "coudot";
- $oldpassword = "secret";
-
- if ( version_compare(PHP_VERSION, '7.2.5') >= 0 ) {
- $pwd_policy_config = array(
- "pwd_show_policy" => true,
- "pwd_min_length" => 6,
- "pwd_max_length" => 12,
- "pwd_min_lower" => 1,
- "pwd_min_upper" => 1,
- "pwd_min_digit" => 1,
- "pwd_min_special" => 1,
- "pwd_special_chars" => "^a-zA-Z0-9",
- "pwd_forbidden_chars" => "@",
- "pwd_no_reuse" => true,
- "pwd_diff_last_min_chars" => 0,
- "pwd_diff_login" => true,
- "pwd_complexity" => 0,
- "use_pwnedpasswords" => true,
- "pwd_no_special_at_ends" => false,
- "pwd_forbidden_words" => array(),
- "pwd_forbidden_ldap_fields"=> array(),
- );
-
- $this->assertEquals("pwned", check_password_strength( "!1Password", $oldpassword, $pwd_policy_config, $login, array(), array() ) );
- }
-
- }
-
- /**
- * Test check_password_strength function with weak entropy password
- */
- public function testCheckPasswordStrengthWeakEntropy()
- {
-
- # Load functions
- require_once __DIR__ . '/../lib/functions.inc.php';
-
- $login = "johnsmith";
- $oldpassword = "secret";
-
- if ( version_compare(PHP_VERSION, '7.2.5') >= 0 ) {
- $pwd_policy_config = array(
- "pwd_show_policy" => true,
- "pwd_min_length" => 6,
- "pwd_max_length" => 0,
- "pwd_min_lower" => 0,
- "pwd_min_upper" => 0,
- "pwd_min_digit" => 0,
- "pwd_min_special" => 0,
- "pwd_special_chars" => "^a-zA-Z0-9",
- "pwd_forbidden_chars" => "",
- "pwd_no_reuse" => false,
- "pwd_diff_last_min_chars" => 0,
- "pwd_diff_login" => false,
- "pwd_complexity" => 0,
- "use_pwnedpasswords" => false,
- "pwd_no_special_at_ends" => false,
- "pwd_forbidden_words" => array(),
- "pwd_forbidden_ldap_fields"=> array(),
- "pwd_display_entropy" => true,
- "pwd_check_entropy" => true,
- "pwd_min_entropy" => 3
- );
-
- $this->assertEquals("insufficiententropy", check_password_strength( "secret", $oldpassword, $pwd_policy_config, $login, array(), array() ) );
- }
-
- }
-
- /**
- * Test check_password_strength function with strong entropy password
- */
- public function testCheckPasswordStrengthStrongEntropy()
- {
-
- # Load functions
- require_once __DIR__ . '/../lib/functions.inc.php';
-
- $login = "johnsmith";
- $oldpassword = "secret";
-
- if ( version_compare(PHP_VERSION, '7.2.5') >= 0 ) {
- $pwd_policy_config = array(
- "pwd_show_policy" => true,
- "pwd_min_length" => 6,
- "pwd_max_length" => 0,
- "pwd_min_lower" => 0,
- "pwd_min_upper" => 0,
- "pwd_min_digit" => 0,
- "pwd_min_special" => 0,
- "pwd_special_chars" => "^a-zA-Z0-9",
- "pwd_forbidden_chars" => "",
- "pwd_no_reuse" => false,
- "pwd_diff_last_min_chars" => 0,
- "pwd_diff_login" => false,
- "pwd_complexity" => 0,
- "use_pwnedpasswords" => false,
- "pwd_no_special_at_ends" => false,
- "pwd_forbidden_words" => array(),
- "pwd_forbidden_ldap_fields"=> array(),
- "pwd_display_entropy" => true,
- "pwd_check_entropy" => true,
- "pwd_min_entropy" => 3
- );
-
- $this->assertEquals("", check_password_strength( "Th!Sis@Str0ngP@ss0rd", $oldpassword, $pwd_policy_config, $login, array(), array() ) );
- }
-
- }
-
-}