diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index c87bb34de8..2e3144c3ce 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -406,16 +406,11 @@ def get_perm_info(arg=None): @frappe.whitelist(allow_guest=True) def update_password(new_password, key=None, old_password=None): - # verify old password - if key: - user = frappe.db.get_value("User", {"reset_password_key":key}) - if not user: - return _("Cannot Update: Incorrect / Expired Link.") - - elif old_password: - # verify old password - frappe.local.login_manager.check_password(frappe.session.user, old_password) - user = frappe.session.user + res = _get_user_for_update_password(key, old_password) + if res.get('message'): + return res['message'] + else: + user = res['user'] _update_password(user, new_password) @@ -428,6 +423,44 @@ def update_password(new_password, key=None, old_password=None): else: return redirect_url if redirect_url else "/" +@frappe.whitelist(allow_guest=True) +def test_password_strength(new_password, key=None, old_password=None): + from frappe.utils.password_strength import test_password_strength as _test_password_strength + + res = _get_user_for_update_password(key, old_password) + if not res: + return + elif res.get('message'): + return res['message'] + else: + user = res['user'] + + user_data = frappe.db.get_value('User', user, ['first_name', 'middle_name', 'last_name', 'email', 'birth_date']) + + if new_password: + return _test_password_strength(new_password, user_inputs=user_data) + +def _get_user_for_update_password(key, old_password): + # verify old password + if key: + user = frappe.db.get_value("User", {"reset_password_key": key}) + if not user: + return { + 'message': _("Cannot Update: Incorrect / Expired Link.") + } + + elif old_password: + # verify old password + frappe.local.login_manager.check_password(frappe.session.user, old_password) + user = frappe.session.user + + else: + return + + return { + 'user': user + } + def reset_user_data(user): user_doc = frappe.get_doc("User", user) redirect_url = user_doc.redirect_url diff --git a/frappe/templates/pages/update-password.html b/frappe/templates/pages/update-password.html index 9960b82a9d..fb02197871 100644 --- a/frappe/templates/pages/update-password.html +++ b/frappe/templates/pages/update-password.html @@ -15,7 +15,9 @@
+
+
@@ -31,9 +33,9 @@ frappe.ready(function() { $("#old_password").parent().toggle(false); } - $("#reset-password").on("submit", function() { - return false; - }); + $("#reset-password").on("submit", function() { + return false; + }); $("#new_password").on("keypress", function(e) { if(e.which===13) $("#update").click(); @@ -76,7 +78,99 @@ frappe.ready(function() { return false; }); + + window.strength_indicator = $('.password-strength-indicator'); + window.strength_message = $('.password-strength-message'); + + $('#new_password').on('keyup', function() { + window.clear_timeout(); + window.timout_password_strength = setTimeout(window.test_password_strength, 200); + }); + + window.test_password_strength = function() { + window.timout_password_strength = null; + + var args = { + key: get_url_arg("key") || "", + old_password: $("#old_password").val(), + new_password: $("#new_password").val() + } + + if (!args.new_password) { + set_strength_indicator('grey', {'warning': __('Please enter the password') }); + return; + } + + return frappe.call({ + type: 'GET', + method: 'frappe.core.doctype.user.user.test_password_strength', + args: args, + callback: function(r) { + if (r.message && r.message.entropy) { + var score = r.message.score, + feedback = r.message.feedback; + + feedback.crack_time_display = r.message.crack_time_display; + feedback.score = score; + + if (score < 2) { + set_strength_indicator('red', feedback); + } else if (score < 4) { + set_strength_indicator('yellow', feedback); + } else { + set_strength_indicator('green', feedback); + } + } + // console.log(r.message); + } + }); + }; + + window.set_strength_indicator = function(color, feedback) { + var message = []; + if (feedback) { + if (feedback.suggestions && feedback.suggestions.length) { + message = message.concat(feedback.suggestions); + } else if (feedback.warning) { + message.push(feedback.warning); + } + + if (!message.length && feedback.crack_time_display) { + message.push(__('This password will take {0} to crack', [feedback.crack_time_display])); + if (feedback.score > 3) { + message.push('👍'); + } + } + } + + strength_indicator.removeClass().addClass('password-strength-indicator indicator ' + color); + strength_message.text(message.join(' ') || '').removeClass('hidden'); + // strength_indicator.attr('title', message.join(' ') || ''); + } + + window.clear_timeout = function() { + if (window.timout_password_strength) { + clearTimeout(window.timout_password_strength); + window.timout_password_strength = null; + } + }; }); + +{% endblock %} + +{% block style %} + {% endblock %} diff --git a/frappe/utils/password_strength.py b/frappe/utils/password_strength.py index adf60f6e16..bc0b6ad8c3 100644 --- a/frappe/utils/password_strength.py +++ b/frappe/utils/password_strength.py @@ -50,7 +50,7 @@ def get_feedback (score, sequence): feedback = { "warning": "", "suggestions":[ - _("Add another word or two. Uncommon words are better.") + _("Better add a few more letters or another word") ], } return feedback @@ -68,40 +68,39 @@ def get_match_feedback(match, is_sole_match): def fun_spatial(): if match["turns"] == 1: feedback ={ - "warning": _('Straight rows of keys are easy to guess.'), + "warning": _('Straight rows of keys are easy to guess'), "suggestions":[ - _("Use a longer keyboard pattern with more turns.") + _("Try to use a longer keyboard pattern with more turns") ], } else: feedback ={ - "warning": _('Short keyboard patterns are easy to guess.'), + "warning": _('Short keyboard patterns are easy to guess'), "suggestions":[ - _("Use a longer keyboard pattern with more turns.") + _("Make use of longer keyboard patterns") ], } return feedback def fun_repeat(): if len(match["repeated_char"]) == 1: feedback ={ - "warning": _('Repeats like "aaa" are easy to guess.'), + "warning": _('Repeats like "aaa" are easy to guess'), "suggestions":[ - _("Avoid repeated words and characters.") + _("Let's avoid repeated words and characters") ], } else: feedback ={ "warning": _('Repeats like "abcabcabc" are only slightly harder to guess than "abc"'), "suggestions":[ - _("Avoid repeated words and characters.") + _("Try to avoid repeated words and characters") ], } return feedback def fun_sequence(): return { - "warning": _("Sequences like abc or 6543 are easy to guess."), "suggestions":[ - _("Avoid sequences.") + _("Avoid sequences like abc or 6543 as they are easy to guess") ], } def fun_regex():