Skip to content

Commit

Permalink
added password strength checker and new option para.min_password_stre…
Browse files Browse the repository at this point in the history
…ngth to enforce password strength
  • Loading branch information
albogdano committed Jul 30, 2021
1 parent 5400096 commit 3e081e6
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 10 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -211,9 +211,9 @@ para.admins = "admin@domain.com"
# enable or disable email and password authentication
para.password_auth_enabled = true
# min. password length
para.min_password_length = 7
para.min_password_length = 8
# min. password strength (1=Good, 2=Strong, 3=Very Strong)
para.min_password_strength = 1
para.min_password_strength = 2
# Session cookie name
para.auth_cookie = "scoold-auth"
# Facebook - create your own Facebook app first!
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/erudika/scoold/ScooldServer.java
Expand Up @@ -83,6 +83,8 @@ public class ScooldServer extends SpringBootServletInitializer {
public static final int MAX_TAGS_PER_POST = Config.getConfigInt("max_tags_per_post", 5);
public static final int MAX_REPLIES_PER_POST = Config.getConfigInt("max_replies_per_post", 500);
public static final int MAX_FAV_TAGS = Config.getConfigInt("max_fav_tags", 50);
public static final int MIN_PASS_LENGTH = Config.getConfigInt("min_password_length", 8);
public static final int MIN_PASS_STRENGTH = Config.getConfigInt("min_password_strength", 2);

public static final int ANSWER_VOTEUP_REWARD_AUTHOR = Config.getConfigInt("answer_voteup_reward_author", 10);
public static final int QUESTION_VOTEUP_REWARD_AUTHOR = Config.getConfigInt("question_voteup_reward author", 5);
Expand Down
46 changes: 42 additions & 4 deletions src/main/java/com/erudika/scoold/controllers/SigninController.java
Expand Up @@ -24,6 +24,8 @@
import com.erudika.para.utils.Config;
import com.erudika.para.utils.Utils;
import static com.erudika.scoold.ScooldServer.HOMEPAGE;
import static com.erudika.scoold.ScooldServer.MIN_PASS_LENGTH;
import static com.erudika.scoold.ScooldServer.MIN_PASS_STRENGTH;
import static com.erudika.scoold.ScooldServer.SIGNINLINK;
import com.erudika.scoold.utils.HttpUtils;
import com.erudika.scoold.utils.ScooldUtils;
Expand Down Expand Up @@ -150,7 +152,8 @@ public String signup(@RequestParam String name, @RequestParam String email, @Req
HttpServletRequest req, HttpServletResponse res, Model model) {
boolean approvedDomain = utils.isEmailDomainApproved(email);
if (!utils.isAuthenticated(req) && approvedDomain) {
if (!isEmailRegistered(email) && isSubmittedByHuman(req)) {
boolean goodPass = isPasswordStrongEnough(passw);
if (!isEmailRegistered(email) && isSubmittedByHuman(req) && goodPass) {
User u = pc.signIn("password", email + ":" + name + ":" + passw, false);
if (u != null && u.getActive()) {
setAuthCookie(u.getPassword(), req, res);
Expand All @@ -166,7 +169,11 @@ public String signup(@RequestParam String name, @RequestParam String email, @Req
model.addAttribute("name", name);
model.addAttribute("bademail", email);
model.addAttribute("emailPattern", Email.EMAIL_PATTERN);
model.addAttribute("error", Collections.singletonMap("email", utils.getLang(req).get("msgcode.1")));
if (!goodPass) {
model.addAttribute("error", Collections.singletonMap("passw", utils.getLang(req).get("msgcode.8")));
} else {
model.addAttribute("error", Collections.singletonMap("email", utils.getLang(req).get("msgcode.1")));
}
return "base";
}
}
Expand Down Expand Up @@ -222,7 +229,11 @@ public String changePass(@RequestParam String email,
model.addAttribute("token", "");
model.addAttribute("verified", !error);
if (error) {
model.addAttribute("error", Collections.singletonMap("email", utils.getLang(req).get("msgcode.7")));
if (!isPasswordStrongEnough(newpassword)) {
model.addAttribute("error", Collections.singletonMap("newpassword", utils.getLang(req).get("msgcode.8")));
} else {
model.addAttribute("error", Collections.singletonMap("email", utils.getLang(req).get("msgcode.7")));
}
}
return "base";
}
Expand Down Expand Up @@ -317,6 +328,33 @@ private boolean isSubmittedByHuman(HttpServletRequest req) {
return StringUtils.isBlank(req.getParameter("leaveblank")) && (System.currentTimeMillis() - time >= 7000);
}

private boolean isPasswordStrongEnough(String password) {
if (StringUtils.length(password) >= MIN_PASS_LENGTH) {
int score = 0;
if (password.matches(".*[a-z].*")) {
score++;
}
if (password.matches(".*[A-Z].*")) {
score++;
}
if (password.matches(".*[0-9].*")) {
score++;
}
if (password.matches(".*[^\\w\\s\\n\\t].*")) {
score++;
}
// 1 = good strength, 2 = medium strength, 3 = high strength
if (MIN_PASS_STRENGTH <= 1) {
return score >= 2;
} else if (MIN_PASS_STRENGTH == 2) {
return score >= 3;
} else {
return score >= 4;
}
}
return false;
}

private String generatePasswordResetToken(String email, HttpServletRequest req) {
if (StringUtils.isBlank(email)) {
return "";
Expand All @@ -334,7 +372,7 @@ private String generatePasswordResetToken(String email, HttpServletRequest req)
}

private boolean resetPassword(String email, String newpass, String token) {
if (StringUtils.isBlank(newpass) || StringUtils.isBlank(token) || newpass.length() < Config.MIN_PASS_LENGTH) {
if (StringUtils.isBlank(newpass) || StringUtils.isBlank(token) || !isPasswordStrongEnough(newpass)) {
return false;
}
Sysprop s = pc.read(email);
Expand Down
Expand Up @@ -121,6 +121,7 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response,
modelAndView.addObject("MAX_TAGS_PER_POST", MAX_TAGS_PER_POST);
modelAndView.addObject("MAX_REPLIES_PER_POST", MAX_REPLIES_PER_POST);
modelAndView.addObject("MAX_FAV_TAGS", MAX_FAV_TAGS);
modelAndView.addObject("MIN_PASS_LENGTH", MIN_PASS_LENGTH);
modelAndView.addObject("ANSWER_VOTEUP_REWARD_AUTHOR", ANSWER_VOTEUP_REWARD_AUTHOR);
modelAndView.addObject("QUESTION_VOTEUP_REWARD_AUTHOR", QUESTION_VOTEUP_REWARD_AUTHOR);
modelAndView.addObject("VOTEUP_REWARD_AUTHOR", VOTEUP_REWARD_AUTHOR);
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/lang_en.properties
Expand Up @@ -325,6 +325,7 @@ msgcode.4 = Your account has been deleted.
msgcode.5 = You have been signed out.
msgcode.6 = Your email address has not been confirmed yet.
msgcode.7 = Something went terribly wrong!
msgcode.8 = Password is not strong enough.
msgcode.16 = This post was deleted.

about.scoold.1 = Scoold is an open source forum platform for knowledge sharing and collaboration. It was inspired by StackOverflow.com the sites of the Stack Exchange family and we have huge respect towards the people behind them.
Expand Down
63 changes: 62 additions & 1 deletion src/main/resources/static/scripts/scoold.js
Expand Up @@ -15,7 +15,7 @@
*
* For issues and patches go to: https://github.com/erudika
*/
/*global window: false, jQuery: false, $: false, google, hljs, RTL_ENABLED, CONTEXT_PATH, M, CONFIRM_MSG, WELCOME_MESSAGE, WELCOME_MESSAGE_ONLOGIN, MAX_TAGS_PER_POST: false */
/*global window: false, jQuery: false, $: false, google, hljs, RTL_ENABLED, CONTEXT_PATH, M, CONFIRM_MSG, WELCOME_MESSAGE, WELCOME_MESSAGE_ONLOGIN, MAX_TAGS_PER_POST, MIN_PASS_LENGTH: false */
"use strict";
$(function () {
var mapCanvas = $("div#map-canvas");
Expand Down Expand Up @@ -1188,4 +1188,65 @@ $(function () {
return true;
});
}

if (window.location.pathname.indexOf(CONTEXT_PATH + '/signin/') >= 0) {
var passwordInput = $('#passw, #newpassword');
console.log(passwordInput);
var scoreMessage = $('#pass-meter-message');
var messagesList = ['Too simple', 'Weak', 'Good', 'Strong', 'Very strong'];
passwordInput.on('keyup', function () {
var score = 0;
var val = passwordInput.val();
if (val.length >= (MIN_PASS_LENGTH || 8)) {
console.log("len");
++score;
}
if (val.match(/(?=.*[a-z])/)) {
console.log("lower");
++score;
}
if (val.match(/(?=.*[A-Z])/)) {
console.log("upper");
++score;
}
if (val.match(/(?=.*[0-9])/)) {
console.log("num");
++score;
}
if (val.match(/(?=.*[^\w\s\n\t])/)) {
console.log("sym");
++score;
}
if (val.length === 0) {
score = -1;
}
switch (score) {
case 0:
case 1:
passwordInput.attr('class', 'pass-meter');
scoreMessage.text(messagesList[0]);
break;
case 2:
passwordInput.attr('class', 'pass-meter psms-25');
scoreMessage.text(messagesList[1]);
break;
case 3:
passwordInput.attr('class', 'pass-meter psms-50');
scoreMessage.text(messagesList[2]);
break;
case 4:
passwordInput.attr('class', 'pass-meter psms-75');
scoreMessage.text(messagesList[3]);
break;
case 5:
passwordInput.attr('class', 'pass-meter psms-100');
scoreMessage.text(messagesList[4]);
break;
default:
passwordInput.attr('class', '');
scoreMessage.text('');
}
});
}

});
6 changes: 6 additions & 0 deletions src/main/resources/static/styles/style.css
Expand Up @@ -392,6 +392,12 @@ img.profile-pic {
display: none;
}

#passw.pass-meter, #newpassword.pass-meter { border-bottom: 3px solid #F44336; box-shadow: none;}
#passw.pass-meter.psms-25, #newpassword.pass-meter.psms-25 { border-bottom: 3px solid #F44336; box-shadow: none; }
#passw.pass-meter.psms-50, #newpassword.pass-meter.psms-50 { border-bottom: 3px solid #FFC000; box-shadow: none; }
#passw.pass-meter.psms-75, #newpassword.pass-meter.psms-75 { border-bottom: 3px solid #b2ca7d; box-shadow: none; }
#passw.pass-meter.psms-100, #newpassword.pass-meter.psms-100 { border-bottom: 3px solid #88AF12; box-shadow: none; }

@media (max-width: 1400px) {
.container {width: 90%;}
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/templates/base.vm
Expand Up @@ -423,11 +423,12 @@
#set($isRTLJS = "RTL_ENABLED = $RTL_ENABLED")
#set($isAdminJS = "IS_ADMIN = $isAdmin")
#set($maxTagsJS = "MAX_TAGS_PER_POST = $MAX_TAGS_PER_POST")
#set($minPassLen = "MIN_PASS_LENGTH = $MIN_PASS_LENGTH")
#set($_welcomeMsg = $scooldUtils.getWelcomeMessage($authUser))
#set($_welcomeMessageJS = "WELCOME_MESSAGE = '$_welcomeMsg'")
#set($_welcomeMsgOnLogin = $scooldUtils.getWelcomeMessageOnLogin($authUser))
#set($_welcomeMessageOnLoginJS = "WELCOME_MESSAGE_ONLOGIN = '$_welcomeMsgOnLogin'")
<script nonce="$cspNonce">$ctxPathJS;$isRTLJS;$isAdminJS;$maxTagsJS;$_welcomeMessageJS;$_welcomeMessageOnLoginJS;</script>
<script nonce="$cspNonce">$ctxPathJS;$isRTLJS;$isAdminJS;$maxTagsJS;$minPassLen;$_welcomeMessageJS;$_welcomeMessageOnLoginJS;</script>
<script nonce="$cspNonce" src="$!scriptslink/scoold.js"></script>

#if ($request.getServletPath() == $signinlink)
Expand Down
8 changes: 6 additions & 2 deletions src/main/resources/templates/signin.vm
Expand Up @@ -43,7 +43,9 @@
#if(!$resend)
<div class="input-field mvm">
<label for="passw">$!lang.get('password')</label>
<input id="passw" type="password" name="passw" value="" minlength="6" required>
<input id="passw" type="password" name="passw" value="" minlength="$MIN_PASS_LENGTH" required>
<div><small id="pass-meter-message" class="grey-text"></small></div>
#getmessagebox("red white-text" $error.get("passw"))
</div>
#end

Expand Down Expand Up @@ -97,7 +99,9 @@
#if($token)
<div class="input-field mvm">
<label for="newpassword">$!lang.get('newpassword')</label>
<input id="newpassword" type="password" name="newpassword" value="" minlength="6" required>
<input id="newpassword" type="password" name="newpassword" value="" minlength="$MIN_PASS_LENGTH" required>
<div><small id="pass-meter-message" class="grey-text"></small></div>
#getmessagebox("red white-text" $error.get("newpassword"))
</div>
#end
#if($token)
Expand Down

0 comments on commit 3e081e6

Please sign in to comment.